Replace Lint.Test by the it's new version

This commit is contained in:
Jeroen Engels 2019-07-02 00:04:45 +02:00
parent bf5a7dad8b
commit 003336a735
7 changed files with 708 additions and 803 deletions

View File

@ -1,13 +1,57 @@
module Lint.Test exposing (LintResult, errorWithoutRange, expectErrors, expectErrorsWithoutRange, location, run)
module Lint.Test exposing
( LintResult, run
, ExpectedError, expectErrors, expectNoErrors, error, atExactly
)
{-| Module that helps you test your linting rules, using [`elm-test`](https://package.elm-lang.org/packages/elm-explorations/test/latest).
TODO Add instructions and examples
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
import The.Rule.You.Want.To.Test exposing (rule)
testRule : String -> LintResult
testRule string =
Lint.Test.run rule string
-- In this example, the rule we're testing is `NoDebug`
tests : Test
tests =
describe "NoDebug"
[ test "should not report calls to normal functions" <|
\() ->
testRule """module A exposing (..)
a = foo n"""
|> Lint.Test.expectNoErrors
, test "should report Debug.log use" <|
\() ->
testRule """module A exposing (..)
a = Debug.log "some" "message\""""
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Forbidden use of Debug"
, under = "Debug.log"
}
]
]
# Running tests
@docs LintResult, run
# Making assertions
@docs ExpectedError, expectErrors, expectNoErrors, error, atExactly
# Tips on testing
## What should you test?
TODO Add helpful tips
TODO Rework API, we can do something much nicer than this
-}
import Array exposing (Array)
@ -15,79 +59,508 @@ import Elm.Syntax.Range exposing (Range)
import Expect exposing (Expectation)
import Lint exposing (Severity(..), lintSource)
import Lint.Rule as Rule exposing (Error, Rule)
import List.Extra
{-| Alias for the result of a lint rule being applied on a string containing Elm code.
{-| The result of running a rule on a `String` containing source code.
-}
type alias LintResult =
Result (List String) (List Error)
type LintResult
= ParseFailure
| SuccessfulRun CodeInspector (List Error)
{-| Run a `Rule` on a string and get the errors reported by it. If the string is
not valid Elm code, this will return a `Result` of type `Err`.
type alias CodeInspector =
{ getCodeAtLocation : Range -> Maybe String
, checkIfLocationIsAmbiguous : Error -> String -> Expectation
}
Note that to be valid, a code needs to start with a module definition followed by
a line break like `module A exposing (..)\n`.
{-| An expectation for an error. Use [`error`](#error) to create one.
-}
type ExpectedError
= ExpectedError
{ message : String
, under : Under
}
type Under
= Under String
| UnderExactly String Range
type SourceCode
= SourceCode String
{-| Run a `Rule` on a `String` containing source code. You can then use
[`expectNoErrors`](#expectNoErrors) or [`expectErrors`](#expectErrors) to assert
the errors reported by the rule.
The source code needs to be syntactically valid Elm code. If the code
can't be parsed, the test will fail regardless of the expectations you set on it.
Note that t be syntactically valid, you need at least a module declaration at the
top of the file (like `module A exposing (..)`) and one declaration (like `a = 1`).
You can't just have an expression like `1 + 2`.
-}
run : Rule -> String -> LintResult
run rule sourceCode =
case lintSource [ ( Critical, rule ) ] sourceCode of
Ok errors ->
SuccessfulRun
{ getCodeAtLocation = getCodeAtLocationInSourceCode (SourceCode sourceCode)
, checkIfLocationIsAmbiguous = checkIfLocationIsAmbiguousInSourceCode (SourceCode sourceCode)
}
(List.map (\( _, error_ ) -> Rule.error error_.message error_.range) errors)
Err _ ->
ParseFailure
{-| Assert that the rule reprted no errors. Note, this is equivalent to using [`expectErrors`](#expectErrors)
like `expectErrors []`.
import Lint.Test exposing (LintResult)
import Test
import Test exposing (Test, describe, test)
import The.Rule.You.Want.To.Test exposing (rule)
testRule : String -> LintResult
testRule string =
"module A exposing (..)\n\n"
++ string
|> Lint.Test.run rule
Lint.Test.run rule string
tests : List Test
-- In this example, the rule we're testing is `NoDebug`
tests : Test
tests =
[ test "should not report normal function calls" <|
\() ->
testRule "a = Debug.log"
|> Lint.Test.expectErrors
[ error (Lint.Test.location ( 3, 5 ) ( 3, 14 )) ]
]
describe "NoDebug"
[ test "should not report calls to normal functions" <|
\() ->
testRule """module A exposing (..)
a = foo n"""
|> Lint.Test.expectNoErrors
]
-}
run : Rule -> String -> Result (List String) (List Error)
run rule str =
lintSource [ ( Critical, rule ) ] str
|> Result.map (List.map (\( severity, { message, range } ) -> Rule.error message range))
expectNoErrors : LintResult -> Expectation
expectNoErrors lintResult =
case lintResult of
ParseFailure ->
Expect.fail parsingErrorMessage
SuccessfulRun _ errors ->
Expect.true
("I expected no errors but found:\n\n" ++ (List.map errorToString errors |> String.join "\n"))
(List.isEmpty errors)
expectErrors : List Error -> LintResult -> Expectation
expectErrors expectedErrors result =
case result of
Err errors ->
Expect.fail <| String.join "\n" errors
{-| Assert that the rule reprted some errors, by specifying which one.
Ok errors ->
Expect.equal expectedErrors errors
Assert which errors are reported using [`error`](#error). The test will fail if
a different number of errors than expected are reported, or if the message or the
location is incorrect.
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
import The.Rule.You.Want.To.Test exposing (rule)
testRule : String -> LintResult
testRule string =
Lint.Test.run rule string
-- In this example, the rule we're testing is `NoDebug`
tests : Test
tests =
describe "NoDebug"
[ test "should report Debug.log use" <|
\() ->
testRule """module A exposing (..)
a = Debug.log "some" "message\""""
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Forbidden use of Debug"
, under = "Debug.log"
}
]
]
-}
expectErrors : List ExpectedError -> LintResult -> Expectation
expectErrors expectedErrors lintResult =
case lintResult of
ParseFailure ->
Expect.fail parsingErrorMessage
SuccessfulRun codeInspector errors ->
checkAllErrorsMatch codeInspector expectedErrors errors
expectErrorsWithoutRange : List Error -> LintResult -> Expectation
expectErrorsWithoutRange expectedErrors result =
case result of
Err errors ->
Expect.fail <| String.join "\n" errors
{-| Create an expectation for an error.
Ok errors ->
Expect.equal
(errorMessages expectedErrors)
(errorMessages errors)
`message` should be the message you're expecting to be shown to the user.
`under` is the part of the code where you are expecting the error to be shown to
the user. If it helps, imagine `under` to be the text under which the squiggly
lines will appear if the error appeared in an editor.
tests : Test
tests =
describe "NoDebug"
[ test "should report Debug.log use" <|
\() ->
testRule """module A exposing (..)
a = Debug.log "some" "message\""""
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Forbidden use of Debug"
, under = "Debug.log"
}
]
]
If there are multiple locations where the value of `under` appears, the test will
fail unless you use [`atExactly`](#atExactly) to remove any ambiguity of where the
error should be used.
-}
error : { message : String, under : String } -> ExpectedError
error input =
ExpectedError
{ message = input.message
, under = Under input.under
}
errorMessages : List Error -> List String
errorMessages errors =
List.map Rule.errorMessage errors
getUnder : ExpectedError -> String
getUnder (ExpectedError expectedError) =
case expectedError.under of
Under str ->
str
UnderExactly str _ ->
str
errorWithoutRange : String -> Error
errorWithoutRange message =
Rule.error message (location ( 0, 0 ) ( 0, 0 ))
{-| Precise the exact position where the error should be shown to the user. This
is only necessary when the `under` field is ambiguous.
`atExactly` takes a record with start and end positions.
tests : Test
tests =
describe "NoDebug"
[ test "should report multiple Debug.log calls" <|
\() ->
testRule """
a = Debug.log z
b = Debug.log z
"""
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
|> Lint.Test.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }
, Lint.Test.error
{ message = message
, under = "Debug.log"
}
|> Lint.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }
]
]
Tip: By default, do not provide this field. If the test fails because there is some
ambiguity, the test error will give you a recommendation of what to use as a parameter
of `atExactly`, so you do not have to bother writing this hard to write argument.
-}
atExactly : { start : { row : Int, column : Int }, end : { row : Int, column : Int } } -> ExpectedError -> ExpectedError
atExactly range ((ExpectedError expectedError_) as expectedError) =
ExpectedError { expectedError_ | under = UnderExactly (getUnder expectedError) range }
location : ( Int, Int ) -> ( Int, Int ) -> Range
location ( rowStart, columnStart ) ( rowEnd, columnEnd ) =
{ start = { row = rowStart, column = columnStart }
, end = { row = rowEnd, column = columnEnd }
checkAllErrorsMatch : CodeInspector -> List ExpectedError -> List Error -> Expectation
checkAllErrorsMatch codeInspector expectedErrors errors =
checkErrorsMatch codeInspector expectedErrors errors
|> List.reverse
|> (\expectations -> Expect.all expectations ())
checkErrorsMatch : CodeInspector -> List ExpectedError -> List Error -> List (() -> Expectation)
checkErrorsMatch codeInspector expectedErrors errors =
case ( expectedErrors, errors ) of
( [], [] ) ->
[ always Expect.pass ]
( expected :: restOfExpectedErrors, error_ :: restOfErrors ) ->
checkErrorMatch codeInspector expected error_ :: checkErrorsMatch codeInspector restOfExpectedErrors restOfErrors
( expected :: restOfExpectedErrors, [] ) ->
[ always <| Expect.fail <| notEnoughErrors expected restOfExpectedErrors ]
( [], error_ :: restOfErrors ) ->
[ always <| Expect.fail <| tooManyErrors error_ restOfErrors ]
checkErrorMatch : CodeInspector -> ExpectedError -> Error -> (() -> Expectation)
checkErrorMatch codeInspector ((ExpectedError expectedError_) as expectedError) error_ =
Expect.all
[ always <| Expect.true (messageMismatchError expectedError error_) (expectedError_.message == Rule.errorMessage error_)
, checkMessageAppearsUnder codeInspector error_ expectedError
]
checkMessageAppearsUnder : CodeInspector -> Error -> ExpectedError -> (() -> Expectation)
checkMessageAppearsUnder codeInspector error_ (ExpectedError expectedError) =
case codeInspector.getCodeAtLocation (Rule.errorRange error_) of
Just codeAtLocation ->
case expectedError.under of
Under under ->
Expect.all
[ always <| Expect.true (underMismatchError error_ under codeAtLocation) (codeAtLocation == under)
, always <| codeInspector.checkIfLocationIsAmbiguous error_ under
]
UnderExactly under range ->
Expect.all
[ always <| Expect.true (underMismatchError error_ under codeAtLocation) (codeAtLocation == under)
, always <| Expect.true (wrongLocationError error_ range under) (Rule.errorRange error_ == range)
]
Nothing ->
always <| Expect.fail impossibleStateError
getCodeAtLocationInSourceCode : SourceCode -> Range -> Maybe String
getCodeAtLocationInSourceCode (SourceCode sourceCode) =
let
lines : Array String
lines =
String.lines sourceCode
|> Array.fromList
in
\{ start, end } ->
if start.row == end.row then
Array.get (start.row - 1) lines
|> Maybe.map (String.slice (start.column - 1) (end.column - 1))
else
let
firstLine : Maybe String
firstLine =
Array.get (start.row - 1) lines
|> Maybe.map (String.dropLeft (start.column - 1))
lastLine : Maybe String
lastLine =
Array.get (end.row - 1) lines
|> Maybe.map (String.dropRight end.column)
in
[ [ firstLine ]
, Array.slice start.row (end.row - 1) lines
|> Array.toList
|> List.map Just
, [ lastLine ]
]
|> List.concat
|> List.filterMap identity
|> String.join "\n"
|> Just
formatSourceCode : String -> String
formatSourceCode string =
let
lines =
String.lines string
in
if List.length lines == 1 then
"`" ++ string ++ "`"
else
lines
|> List.map (\str -> " " ++ str)
|> String.join "\n"
|> (\str -> "\n\n```\n" ++ str ++ "\n```")
checkIfLocationIsAmbiguousInSourceCode : SourceCode -> Error -> String -> Expectation
checkIfLocationIsAmbiguousInSourceCode ((SourceCode sourceCodeContent) as sourceCode) error_ under =
let
occurrencesInSourceCode : List Int
occurrencesInSourceCode =
String.indexes under sourceCodeContent
in
Expect.true
(locationIsAmbiguousInSourceCodeError sourceCode error_ under occurrencesInSourceCode)
(List.length occurrencesInSourceCode == 1)
-- ERROR MESSAGES
parsingErrorMessage : String
parsingErrorMessage =
"""I could not parse the test source code, because it was not syntactically valid Elm code.
Maybe you forgot to add the module definition at the top, like:
module A exposing (..)"""
messageMismatchError : ExpectedError -> Error -> String
messageMismatchError (ExpectedError expectedError) error_ =
"""I was looking for the error with the following message:
`""" ++ expectedError.message ++ """`
but I found the following error message:
`""" ++ Rule.errorMessage error_ ++ "`"
wrongLocationError : Error -> Range -> String -> String
wrongLocationError error_ range under =
"""I was looking for the error with the following message:
`""" ++ Rule.errorMessage error_ ++ """`
under the following code:
""" ++ formatSourceCode under ++ """
and I found it. But there are multiple occurrences for this piece of code, and the exact location you specified is not the one I found. I was expecting the error at:
""" ++ rangeAsString range ++ """
but I found it at:
""" ++ rangeAsString (Rule.errorRange error_)
underMismatchError : Error -> String -> String -> String
underMismatchError error_ under codeAtLocation =
"""I found an error with the right message, but at the wrong location:
Message: `""" ++ Rule.errorMessage error_ ++ """`
I saw it under: """ ++ formatSourceCode codeAtLocation ++ """
But I expected to see it under: """ ++ formatSourceCode under
listOccurrencesAsLocations : SourceCode -> String -> List Int -> String
listOccurrencesAsLocations sourceCode under occurrences =
occurrences
|> List.map
(\occurrence ->
occurrence
|> positionAsRange sourceCode under
|> rangeAsString
|> (++) " - "
)
|> String.join "\n"
positionAsRange : SourceCode -> String -> Int -> Range
positionAsRange (SourceCode sourceCode) under position =
let
linesBeforeAndIncludingPosition : List String
linesBeforeAndIncludingPosition =
sourceCode
|> String.slice 0 position
|> String.lines
startRow : Int
startRow =
List.length linesBeforeAndIncludingPosition
in
-- TODO Get these values right
{ start =
{ row = startRow
, column = -1
}
, end =
{ row = startRow + (under |> String.indexes "\n" |> List.length)
, column = under |> String.indexes "\n" |> List.length
}
}
rangeAsString : Range -> String
rangeAsString { start, end } =
"{ start = { row = " ++ String.fromInt start.row ++ ", column = " ++ String.fromInt start.column ++ " }, end = { row = " ++ String.fromInt end.row ++ ", column = " ++ String.fromInt end.column ++ " } }"
errorToString : Error -> String
errorToString error_ =
"- \"" ++ Rule.errorMessage error_ ++ "\" at " ++ Debug.toString (Rule.errorRange error_)
notEnoughErrors : ExpectedError -> List ExpectedError -> String
notEnoughErrors expected restOfExpectedErrors =
let
numberOfErrors : Int
numberOfErrors =
List.length restOfExpectedErrors + 1
in
"I expected to see "
++ String.fromInt numberOfErrors
++ " more "
++ pluralizeErrors numberOfErrors
++ ":\n"
++ (List.map expectedErrorToString (expected :: restOfExpectedErrors) |> String.join "\n")
tooManyErrors : Error -> List Error -> String
tooManyErrors error_ restOfErrors =
let
numberOfErrors : Int
numberOfErrors =
List.length restOfErrors + 1
in
"I found "
++ String.fromInt numberOfErrors
++ " "
++ pluralizeErrors numberOfErrors
++ " too many:\n"
++ (List.map errorToString (error_ :: restOfErrors) |> String.join "\n")
locationIsAmbiguousInSourceCodeError : SourceCode -> Error -> String -> List Int -> String
locationIsAmbiguousInSourceCodeError sourceCode error_ under occurrencesInSourceCode =
"""Your test passes, but where the message appears is ambiguous.
You are looking for the following error message:
`""" ++ Rule.errorMessage error_ ++ """`
and expecting to see it under:
""" ++ formatSourceCode under ++ """
I found """ ++ String.fromInt (List.length occurrencesInSourceCode) ++ """ locations where that code appeared. Please use `Lint.Rule.atExactly` to make the part you were targetting unambiguous.
Tip: I found them at:
""" ++ listOccurrencesAsLocations sourceCode under occurrencesInSourceCode
impossibleStateError : String
impossibleStateError =
"Oh no! I'm in an impossible state. I found an error at a location that I could not find back. Please let me know and give me an SSCCE (http://sscce.org/) here: https://github.com/jfmengels/elm-lint/issues."
pluralizeErrors : Int -> String
pluralizeErrors n =
case n of
1 ->
"error"
_ ->
"errors"
expectedErrorToString : ExpectedError -> String
expectedErrorToString (ExpectedError expectedError) =
"- " ++ expectedError.message

View File

@ -1,568 +0,0 @@
module Lint.Test2 exposing
( LintResult, run
, ExpectedError, expectErrors, expectNoErrors, error, atExactly
)
{-| Module that helps you test your linting rules, using [`elm-test`](https://package.elm-lang.org/packages/elm-explorations/test/latest).
import Lint.Test2 exposing (LintResult)
import Test exposing (Test, describe, test)
import The.Rule.You.Want.To.Test exposing (rule)
testRule : String -> LintResult
testRule string =
Lint.Test2.run rule string
-- In this example, the rule we're testing is `NoDebug`
tests : Test
tests =
describe "NoDebug"
[ test "should not report calls to normal functions" <|
\() ->
testRule """module A exposing (..)
a = foo n"""
|> Lint.Test2.expectNoErrors
, test "should report Debug.log use" <|
\() ->
testRule """module A exposing (..)
a = Debug.log "some" "message\""""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
{ message = "Forbidden use of Debug"
, under = "Debug.log"
}
]
]
# Running tests
@docs LintResult, run
# Making assertions
@docs ExpectedError, expectErrors, expectNoErrors, error, atExactly
TODO Add instructions and examples
# Tips on testing
## What should you test?
TODO Add helpful tips
-}
import Array exposing (Array)
import Elm.Syntax.Range exposing (Range)
import Expect exposing (Expectation)
import Lint exposing (Severity(..), lintSource)
import Lint.Rule as Rule exposing (Error, Rule)
import List.Extra
{-| The result of running a rule on a `String` containing source code.
-}
type LintResult
= ParseFailure
| SuccessfulRun CodeInspector (List Error)
type alias CodeInspector =
{ getCodeAtLocation : Range -> Maybe String
, checkIfLocationIsAmbiguous : Error -> String -> Expectation
}
{-| An expectation of an error. Use [`error`](#error) to create one.
-}
type ExpectedError
= ExpectedError
{ message : String
, under : Under
}
type Under
= Under String
| UnderExactly String Range
type SourceCode
= SourceCode String
{-| Run a `Rule` on a `String` containing source code. You can then use
[`expectNoErrors`](#expectNoErrors) or [`expectErrors`](#expectErrors) to assert
the errors reported by the rule.
The source code needs to be syntactically valid Elm code. If the code
can't be parsed, the test will fail regardless of the expectations you set on it.
Note that t be syntactically valid, you need at least a module declaration at the
top of the file (like `module A exposing (..)`) and one declaration (like `a = 1`).
You can't just have an expression like `1 + 2`.
-}
run : Rule -> String -> LintResult
run rule sourceCode =
case lintSource [ ( Critical, rule ) ] sourceCode of
Ok errors ->
SuccessfulRun
{ getCodeAtLocation = getCodeAtLocationInSourceCode (SourceCode sourceCode)
, checkIfLocationIsAmbiguous = checkIfLocationIsAmbiguousInSourceCode (SourceCode sourceCode)
}
(List.map (\( _, error_ ) -> Rule.error error_.message error_.range) errors)
Err _ ->
ParseFailure
{-| Assert that the rule reprted no errors. Note, this is equivalent to using [`expectErrors`](#expectErrors)
like `expectErrors []`.
import Lint.Test2 exposing (LintResult)
import Test exposing (Test, describe, test)
import The.Rule.You.Want.To.Test exposing (rule)
testRule : String -> LintResult
testRule string =
Lint.Test2.run rule string
-- In this example, the rule we're testing is `NoDebug`
tests : Test
tests =
describe "NoDebug"
[ test "should not report calls to normal functions" <|
\() ->
testRule """module A exposing (..)
a = foo n"""
|> Lint.Test2.expectNoErrors
]
-}
expectNoErrors : LintResult -> Expectation
expectNoErrors lintResult =
case lintResult of
ParseFailure ->
Expect.fail parsingErrorMessage
SuccessfulRun _ errors ->
Expect.true
("I expected no errors but found:\n\n" ++ (List.map errorToString errors |> String.join "\n"))
(List.isEmpty errors)
{-| Assert that the rule reprted some errors, by specifying which one.
Assert which errors are reported using [`error`](#error). The test will fail if
a different number of errors than expected are reported, or if the message or the
location is incorrect.
import Lint.Test2 exposing (LintResult)
import Test exposing (Test, describe, test)
import The.Rule.You.Want.To.Test exposing (rule)
testRule : String -> LintResult
testRule string =
Lint.Test2.run rule string
-- In this example, the rule we're testing is `NoDebug`
tests : Test
tests =
describe "NoDebug"
[ test "should report Debug.log use" <|
\() ->
testRule """module A exposing (..)
a = Debug.log "some" "message\""""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
{ message = "Forbidden use of Debug"
, under = "Debug.log"
}
]
]
-}
expectErrors : List ExpectedError -> LintResult -> Expectation
expectErrors expectedErrors lintResult =
case lintResult of
ParseFailure ->
Expect.fail parsingErrorMessage
SuccessfulRun codeInspector errors ->
checkAllErrorsMatch codeInspector expectedErrors errors
{-| Create an expectation for an error.
`message` should be the message you're expecting to be shown to the user.
`under` is the part of the code where you are expecting the error to be shown to
the user. If it helps, imagine `under` to be the text under which the squiggly
lines will appear if the error appeared in an editor.
tests : Test
tests =
describe "NoDebug"
[ test "should report Debug.log use" <|
\() ->
testRule """module A exposing (..)
a = Debug.log "some" "message\""""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
{ message = "Forbidden use of Debug"
, under = "Debug.log"
}
]
]
If there are multiple locations where the value of `under` appears, the test will
fail unless you use [`atExactly`](#atExactly) to remove any ambiguity of where the
error should be used.
-}
error : { message : String, under : String } -> ExpectedError
error input =
ExpectedError
{ message = input.message
, under = Under input.under
}
getUnder : ExpectedError -> String
getUnder (ExpectedError expectedError) =
case expectedError.under of
Under str ->
str
UnderExactly str _ ->
str
{-| Precise the exact position where the error should be shown to the user. This
is only necessary when the `under` field is ambiguous.
`atExactly` takes a record with start and end positions.
tests : Test
tests =
describe "NoDebug"
[ test "should report multiple Debug.log calls" <|
\() ->
testRule """
a = Debug.log z
b = Debug.log z
"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
{ message = message
, under = "Debug.log"
}
|> Lint.Test2.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }
, Lint.Test2.error
{ message = message
, under = "Debug.log"
}
|> Lint.Test2.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }
]
]
Tip: By default, do not provide this field. If the test fails because there is some
ambiguity, the test error will give you a recommendation of what to use as a parameter
of `atExactly`, so you do not have to bother writing this hard to write argument.
-}
atExactly : { start : { row : Int, column : Int }, end : { row : Int, column : Int } } -> ExpectedError -> ExpectedError
atExactly range ((ExpectedError expectedError_) as expectedError) =
ExpectedError { expectedError_ | under = UnderExactly (getUnder expectedError) range }
checkAllErrorsMatch : CodeInspector -> List ExpectedError -> List Error -> Expectation
checkAllErrorsMatch codeInspector expectedErrors errors =
checkErrorsMatch codeInspector expectedErrors errors
|> List.reverse
|> (\expectations -> Expect.all expectations ())
checkErrorsMatch : CodeInspector -> List ExpectedError -> List Error -> List (() -> Expectation)
checkErrorsMatch codeInspector expectedErrors errors =
case ( expectedErrors, errors ) of
( [], [] ) ->
[ always Expect.pass ]
( expected :: restOfExpectedErrors, error_ :: restOfErrors ) ->
checkErrorMatch codeInspector expected error_ :: checkErrorsMatch codeInspector restOfExpectedErrors restOfErrors
( expected :: restOfExpectedErrors, [] ) ->
[ always <| Expect.fail <| notEnoughErrors expected restOfExpectedErrors ]
( [], error_ :: restOfErrors ) ->
[ always <| Expect.fail <| tooManyErrors error_ restOfErrors ]
checkErrorMatch : CodeInspector -> ExpectedError -> Error -> (() -> Expectation)
checkErrorMatch codeInspector ((ExpectedError expectedError_) as expectedError) error_ =
Expect.all
[ always <| Expect.true (messageMismatchError expectedError error_) (expectedError_.message == Rule.errorMessage error_)
, checkMessageAppearsUnder codeInspector error_ expectedError
]
checkMessageAppearsUnder : CodeInspector -> Error -> ExpectedError -> (() -> Expectation)
checkMessageAppearsUnder codeInspector error_ (ExpectedError expectedError) =
case codeInspector.getCodeAtLocation (Rule.errorRange error_) of
Just codeAtLocation ->
case expectedError.under of
Under under ->
Expect.all
[ always <| Expect.true (underMismatchError error_ under codeAtLocation) (codeAtLocation == under)
, always <| codeInspector.checkIfLocationIsAmbiguous error_ under
]
UnderExactly under range ->
Expect.all
[ always <| Expect.true (underMismatchError error_ under codeAtLocation) (codeAtLocation == under)
, always <| Expect.true (wrongLocationError error_ range under) (Rule.errorRange error_ == range)
]
Nothing ->
always <| Expect.fail impossibleStateError
getCodeAtLocationInSourceCode : SourceCode -> Range -> Maybe String
getCodeAtLocationInSourceCode (SourceCode sourceCode) =
let
lines : Array String
lines =
String.lines sourceCode
|> Array.fromList
in
\{ start, end } ->
if start.row == end.row then
Array.get (start.row - 1) lines
|> Maybe.map (String.slice (start.column - 1) (end.column - 1))
else
let
firstLine : Maybe String
firstLine =
Array.get (start.row - 1) lines
|> Maybe.map (String.dropLeft (start.column - 1))
lastLine : Maybe String
lastLine =
Array.get (end.row - 1) lines
|> Maybe.map (String.dropRight end.column)
in
[ [ firstLine ]
, Array.slice start.row (end.row - 1) lines
|> Array.toList
|> List.map Just
, [ lastLine ]
]
|> List.concat
|> List.filterMap identity
|> String.join "\n"
|> Just
formatSourceCode : String -> String
formatSourceCode string =
let
lines =
String.lines string
in
if List.length lines == 1 then
"`" ++ string ++ "`"
else
lines
|> List.map (\str -> " " ++ str)
|> String.join "\n"
|> (\str -> "\n\n```\n" ++ str ++ "\n```")
checkIfLocationIsAmbiguousInSourceCode : SourceCode -> Error -> String -> Expectation
checkIfLocationIsAmbiguousInSourceCode ((SourceCode sourceCodeContent) as sourceCode) error_ under =
let
occurrencesInSourceCode : List Int
occurrencesInSourceCode =
String.indexes under sourceCodeContent
in
Expect.true
(locationIsAmbiguousInSourceCodeError sourceCode error_ under occurrencesInSourceCode)
(List.length occurrencesInSourceCode == 1)
-- ERROR MESSAGES
parsingErrorMessage : String
parsingErrorMessage =
"""I could not parse the test source code, because it was not syntactically valid Elm code.
Maybe you forgot to add the module definition at the top, like:
module A exposing (..)"""
messageMismatchError : ExpectedError -> Error -> String
messageMismatchError (ExpectedError expectedError) error_ =
"""I was looking for the error with the following message:
`""" ++ expectedError.message ++ """`
but I found the following error message:
`""" ++ Rule.errorMessage error_ ++ "`"
wrongLocationError : Error -> Range -> String -> String
wrongLocationError error_ range under =
"""I was looking for the error with the following message:
`""" ++ Rule.errorMessage error_ ++ """`
under the following code:
""" ++ formatSourceCode under ++ """
and I found it. But there are multiple occurrences for this piece of code, and the exact location you specified is not the one I found. I was expecting the error at:
""" ++ rangeAsString range ++ """
but I found it at:
""" ++ rangeAsString (Rule.errorRange error_)
underMismatchError : Error -> String -> String -> String
underMismatchError error_ under codeAtLocation =
"""I found an error with the right message, but at the wrong location:
Message: `""" ++ Rule.errorMessage error_ ++ """`
I saw it under: """ ++ formatSourceCode codeAtLocation ++ """
But I expected to see it under: """ ++ formatSourceCode under
listOccurrencesAsLocations : SourceCode -> String -> List Int -> String
listOccurrencesAsLocations sourceCode under occurrences =
occurrences
|> List.map
(\occurrence ->
occurrence
|> positionAsRange sourceCode under
|> rangeAsString
|> (++) " - "
)
|> String.join "\n"
positionAsRange : SourceCode -> String -> Int -> Range
positionAsRange (SourceCode sourceCode) under position =
let
linesBeforeAndIncludingPosition : List String
linesBeforeAndIncludingPosition =
sourceCode
|> String.slice 0 position
|> String.lines
startRow : Int
startRow =
List.length linesBeforeAndIncludingPosition
in
-- TODO Get these values right
{ start =
{ row = startRow
, column = -1
}
, end =
{ row = startRow + (under |> String.indexes "\n" |> List.length)
, column = under |> String.indexes "\n" |> List.length
}
}
rangeAsString : Range -> String
rangeAsString { start, end } =
"{ start = { row = " ++ String.fromInt start.row ++ ", column = " ++ String.fromInt start.column ++ " }, end = { row = " ++ String.fromInt end.row ++ ", column = " ++ String.fromInt end.column ++ " } }"
errorToString : Error -> String
errorToString error_ =
"- \"" ++ Rule.errorMessage error_ ++ "\" at " ++ Debug.toString (Rule.errorRange error_)
notEnoughErrors : ExpectedError -> List ExpectedError -> String
notEnoughErrors expected restOfExpectedErrors =
let
numberOfErrors : Int
numberOfErrors =
List.length restOfExpectedErrors + 1
in
"I expected to see "
++ String.fromInt numberOfErrors
++ " more "
++ pluralizeErrors numberOfErrors
++ ":\n"
++ (List.map expectedErrorToString (expected :: restOfExpectedErrors) |> String.join "\n")
tooManyErrors : Error -> List Error -> String
tooManyErrors error_ restOfErrors =
let
numberOfErrors : Int
numberOfErrors =
List.length restOfErrors + 1
in
"I found "
++ String.fromInt numberOfErrors
++ " "
++ pluralizeErrors numberOfErrors
++ " too many:\n"
++ (List.map errorToString (error_ :: restOfErrors) |> String.join "\n")
locationIsAmbiguousInSourceCodeError : SourceCode -> Error -> String -> List Int -> String
locationIsAmbiguousInSourceCodeError sourceCode error_ under occurrencesInSourceCode =
"""Your test passes, but where the message appears is ambiguous.
You are looking for the following error message:
`""" ++ Rule.errorMessage error_ ++ """`
and expecting to see it under:
""" ++ formatSourceCode under ++ """
I found """ ++ String.fromInt (List.length occurrencesInSourceCode) ++ """ locations where that code appeared. Please use `Lint.Rule.atExactly` to make the part you were targetting unambiguous.
Tip: I found them at:
""" ++ listOccurrencesAsLocations sourceCode under occurrencesInSourceCode
impossibleStateError : String
impossibleStateError =
"Oh no! I'm in an impossible state. I found an error at a location that I could not find back. Please let me know and give me an SSCCE (http://sscce.org/) here: https://github.com/jfmengels/elm-lint/issues."
pluralizeErrors : Int -> String
pluralizeErrors n =
case n of
1 ->
"error"
_ ->
"errors"
expectedErrorToString : ExpectedError -> String
expectedErrorToString (ExpectedError expectedError) =
"- " ++ expectedError.message

View File

@ -1,13 +1,13 @@
module DefaultPatternPositionTest exposing (all)
import Lint.Rule.DefaultPatternPosition exposing (PatternPosition(..), rule)
import Lint.Test2 exposing (LintResult)
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
testRule : PatternPosition -> String -> LintResult
testRule patternPosition =
Lint.Test2.run (rule patternPosition)
Lint.Test.run (rule patternPosition)
message : String -> String
@ -26,7 +26,7 @@ a = case b of
Foo -> 1
"""
|> testRule ShouldBeFirst
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report when default pattern is at the expected position (last)" <|
\() ->
"""module A exposing(..)
@ -36,7 +36,7 @@ a = case b of
_ -> 1
"""
|> testRule ShouldBeLast
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report when there is no default pattern (first)" <|
\() ->
"""module A exposing(..)
@ -45,7 +45,7 @@ a = case b of
Bar -> 1
"""
|> testRule ShouldBeFirst
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report when there is no default pattern (last)" <|
\() ->
"""module A exposing(..)
@ -54,7 +54,7 @@ a = case b of
Bar -> 1
"""
|> testRule ShouldBeLast
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report an error when the default pattern is not at the expected position (first) (opposite expected position)" <|
\() ->
"""module A exposing(..)
@ -64,8 +64,8 @@ a = case b of
_ -> 1
"""
|> testRule ShouldBeFirst
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message "first"
, under = "_"
}
@ -79,8 +79,8 @@ a = case b of
Bar -> 1
"""
|> testRule ShouldBeFirst
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message "first"
, under = "_"
}
@ -94,8 +94,8 @@ a = case b of
Bar -> 1
"""
|> testRule ShouldBeLast
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message "last"
, under = "_"
}
@ -109,8 +109,8 @@ a = case b of
Bar -> 1
"""
|> testRule ShouldBeLast
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message "last"
, under = "_"
}

View File

@ -1,7 +1,7 @@
module NoDebugTest exposing (all)
import Lint.Rule.NoDebug exposing (rule)
import Lint.Test2 exposing (LintResult)
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
@ -9,7 +9,7 @@ testRule : String -> LintResult
testRule string =
"module A exposing (..)\n\n"
++ string
|> Lint.Test2.run rule
|> Lint.Test.run rule
message : String
@ -28,12 +28,12 @@ c = debug
e = debug.log n
d = debug.log n
"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report Debug.log use" <|
\() ->
testRule "a = Debug.log"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -41,8 +41,8 @@ d = debug.log n
, test "should report Debug.log calls" <|
\() ->
testRule "a = Debug.log z"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -53,23 +53,23 @@ d = debug.log n
a = Debug.log z
b = Debug.log z
"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
|> Lint.Test2.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }
, Lint.Test2.error
|> Lint.Test.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }
, Lint.Test.error
{ message = message
, under = "Debug.log"
}
|> Lint.Test2.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }
|> Lint.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }
]
, test "should report Debug.crash calls" <|
\() ->
testRule "a = Debug.crash 1"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.crash"
}
@ -77,8 +77,8 @@ b = Debug.log z
, test "should report any Debug method" <|
\() ->
testRule "a = Debug.foo 1"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.foo"
}
@ -86,8 +86,8 @@ b = Debug.log z
, test "should report Debug in a binary expression" <|
\() ->
testRule "a = ( Debug.log z ) + 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -95,8 +95,8 @@ b = Debug.log z
, test "should report Debug in a << binary expression" <|
\() ->
testRule "a = fn << Debug.log"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -104,8 +104,8 @@ b = Debug.log z
, test "should report Debug in a pipe expression" <|
\() ->
testRule "a = fn |> Debug.log z"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -113,8 +113,8 @@ b = Debug.log z
, test "should report Debug in an list expression" <|
\() ->
testRule "a = [ Debug.log z y ]"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -122,8 +122,8 @@ b = Debug.log z
, test "should report Debug in an record expression" <|
\() ->
testRule "a = { foo = Debug.log z y }"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -131,8 +131,8 @@ b = Debug.log z
, test "should report Debug in an record update expression" <|
\() ->
testRule "a = { model | foo = Debug.log z y }"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -140,8 +140,8 @@ b = Debug.log z
, test "should report Debug in an lambda expression" <|
\() ->
testRule "a = (\\foo -> Debug.log z foo)"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -149,8 +149,8 @@ b = Debug.log z
, test "should report Debug in an if expression condition" <|
\() ->
testRule "a = if Debug.log a b then True else False"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -158,8 +158,8 @@ b = Debug.log z
, test "should report Debug in an if expression then branch" <|
\() ->
testRule "a = if True then Debug.log a b else False"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -167,8 +167,8 @@ b = Debug.log z
, test "should report Debug in an if expression else branch" <|
\() ->
testRule "a = if True then True else Debug.log a b"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -179,8 +179,8 @@ b = Debug.log z
a = case Debug.log a b of
_ -> []
"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -191,8 +191,8 @@ a = case Debug.log a b of
a = case a of
_ -> Debug.log a b
"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -203,8 +203,8 @@ a = case a of
a = let b = Debug.log a b
in b
"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}
@ -215,8 +215,8 @@ a = let b = Debug.log a b
a = let b = c
in Debug.log a b
"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = message
, under = "Debug.log"
}

View File

@ -1,7 +1,7 @@
module NoExtraBooleanComparisonTest exposing (all)
import Lint.Rule.NoExtraBooleanComparison exposing (rule)
import Lint.Test2 exposing (LintResult)
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
@ -9,7 +9,7 @@ testRule : String -> LintResult
testRule string =
"module A exposing (..)\n\n"
++ string
|> Lint.Test2.run rule
|> Lint.Test.run rule
tests : List Test
@ -17,7 +17,7 @@ tests =
[ test "should not report condition without an operator" <|
\() ->
testRule "a = if n then 1 else 2"
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report condition with integer operators" <|
\() ->
testRule """
@ -26,16 +26,16 @@ b = if n <= 1 then 1 else 2
c = if n > 1 then 1 else 2
d = if n >= 1 then 1 else 2
"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report condition using `not`" <|
\() ->
testRule "a = if not n then 1 else 2"
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report condition with `expr == True`" <|
\() ->
testRule "a = if b == True then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `True`"
, under = "b == True"
}
@ -43,8 +43,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `True == expr`" <|
\() ->
testRule "a = if True == b then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `True`"
, under = "True == b"
}
@ -52,8 +52,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `expr == False`" <|
\() ->
testRule "a = if b == False then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `False`"
, under = "b == False"
}
@ -61,8 +61,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `False == expr`" <|
\() ->
testRule "a = if False == b then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `False`"
, under = "False == b"
}
@ -70,8 +70,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `expr /= True`" <|
\() ->
testRule "a = if b /= True then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `True`"
, under = "b /= True"
}
@ -79,8 +79,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `True /= expr`" <|
\() ->
testRule "a = if True /= b then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `True`"
, under = "True /= b"
}
@ -88,8 +88,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `expr /= False`" <|
\() ->
testRule "a = if b /= False then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `False`"
, under = "b /= False"
}
@ -97,8 +97,8 @@ d = if n >= 1 then 1 else 2
, test "should report condition with `False /= expr`" <|
\() ->
testRule "a = if False /= b then 1 else 2"
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Unnecessary comparison with `False`"
, under = "False /= b"
}

View File

@ -1,13 +1,13 @@
module NoImportingEverythingTest exposing (all)
import Lint.Rule.NoImportingEverything exposing (Configuration, rule)
import Lint.Test2 exposing (LintResult)
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
testRule : Configuration -> String -> LintResult
testRule options =
Lint.Test2.run (rule options)
Lint.Test.run (rule options)
tests : List Test
@ -18,33 +18,33 @@ tests =
import Html
import Http"""
|> testRule { exceptions = [] }
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report imports that expose functions by name" <|
\() ->
"""module A exposing (..)
import Html exposing (a)
import Http exposing (a, b)"""
|> testRule { exceptions = [] }
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report imports that expose everything" <|
\() ->
"""module A exposing (..)
import Html exposing (..)"""
|> testRule { exceptions = [] }
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Do not expose everything from Html"
, under = ".."
}
|> Lint.Test2.atExactly { start = { row = 2, column = 23 }, end = { row = 2, column = 25 } }
|> Lint.Test.atExactly { start = { row = 2, column = 23 }, end = { row = 2, column = 25 } }
]
, test "should report imports from sub-modules" <|
\() ->
"""module A exposing (a)
import Html.App exposing (..)"""
|> testRule { exceptions = [] }
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Do not expose everything from Html.App"
, under = ".."
}
@ -54,8 +54,8 @@ import Html.App exposing (..)"""
"""module A exposing (a)
import Html.Foo.Bar exposing (..)"""
|> testRule { exceptions = [] }
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Do not expose everything from Html.Foo.Bar"
, under = ".."
}
@ -65,26 +65,26 @@ import Html.Foo.Bar exposing (..)"""
"""module A exposing (a)
import Html exposing (..)"""
|> testRule { exceptions = [ "Html" ] }
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report imports from sub-modules that are in the exception list" <|
\() ->
"""module A exposing (a)
import Html.App exposing (..)"""
|> testRule { exceptions = [ "Html.App" ] }
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report imports from sub-modules (multiple dots)" <|
\() ->
"""module A exposing (a)
import Html.Foo.Bar exposing (..)"""
|> testRule { exceptions = [ "Html.Foo.Bar" ] }
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report imports whose parent is ignored" <|
\() ->
"""module A exposing (a)
import Html.Foo.Bar exposing (..)"""
|> testRule { exceptions = [ "Html" ] }
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Do not expose everything from Html.Foo.Bar"
, under = ".."
}
@ -94,8 +94,8 @@ import Html.Foo.Bar exposing (..)"""
"""module A exposing (a)
import Html exposing (..)"""
|> testRule { exceptions = [ "Html.App" ] }
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Do not expose everything from Html"
, under = ".."
}

View File

@ -1,13 +1,13 @@
module NoUnusedVariablesTest exposing (all)
import Lint.Rule.NoUnusedVariables exposing (rule)
import Lint.Test2 exposing (LintResult)
import Lint.Test exposing (LintResult)
import Test exposing (Test, describe, test)
testRule : String -> LintResult
testRule =
Lint.Test2.run rule
Lint.Test.run rule
tests : List Test
@ -16,19 +16,19 @@ tests =
\() ->
testRule """module A exposing (a)
a = 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report used top-level variables" <|
\() ->
testRule """module A exposing (b)
a n = 1
b = a 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused top-level variables" <|
\() ->
testRule """module A exposing (b)
a = 1"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `a` is not used"
, under = "a"
}
@ -38,33 +38,33 @@ a = 1"""
testRule """module A exposing (b)
a: Int
a = 1"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `a` is not used"
, under = "a"
}
|> Lint.Test2.atExactly { start = { row = 3, column = 1 }, end = { row = 3, column = 2 } }
|> Lint.Test.atExactly { start = { row = 3, column = 1 }, end = { row = 3, column = 2 } }
]
, test "should not report unused top-level variables if everything is exposed" <|
\() ->
testRule """module A exposing (..)
a n = 1
b = a 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report unused top-level variables that are exposed by name" <|
\() ->
testRule """module A exposing (a, b)
a = 1
b = 2"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report unused top-level variables that are exposed by name, but report others" <|
\() ->
testRule """module A exposing (a, b)
a = 1
b = 2
c = 3"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `c` is not used"
, under = "c"
}
@ -74,21 +74,21 @@ c = 3"""
testRule """port module A exposing (..)
a n = 1
b = a 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report unused top-level variables that are exposed by name (port module)" <|
\() ->
testRule """port module A exposing (a, b)
a = 1
b = 2"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report unused top-level variables that are exposed by name, but report others (port module)" <|
\() ->
testRule """port module A exposing (a, b)
a = 1
b = 2
c = 3"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `c` is not used"
, under = "c"
}
@ -98,8 +98,8 @@ c = 3"""
testRule """module A exposing (a)
a = let b = 1
in 2"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `b` is not used"
, under = "b"
}
@ -109,20 +109,20 @@ a = let b = 1
testRule """module A exposing (a, b)
a = let b = 1
in 2"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `b` is not used"
, under = "b"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 9 }, end = { row = 2, column = 10 } }
|> Lint.Test.atExactly { start = { row = 2, column = 9 }, end = { row = 2, column = 10 } }
]
, test "should report unused functions from let even if they are exposed by name" <|
\() ->
testRule """module A exposing (a)
a = let b param = 1
in 2"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `b` is not used"
, under = "b"
}
@ -132,8 +132,8 @@ a = let b param = 1
testRule """module A exposing (..)
a = let b = 1
in 2"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `b` is not used"
, under = "b"
}
@ -144,14 +144,14 @@ a = let b = 1
b = 1
a = let c = 1
in b + c"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report top-level variables used inside let declarations" <|
\() ->
testRule """module A exposing (a)
b = 1
a = let c = b
in c"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report top-level variables used in nested lets" <|
\() ->
testRule """module A exposing (a)
@ -164,24 +164,24 @@ a = let
b + c + e
in
d"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report variables from let declarations that are used in the expression" <|
\() ->
testRule """module A exposing (a)
a = let c = 1
in c"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report unused function parameters" <|
\() ->
testRule """module A exposing (a)
a n = 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused imported functions" <|
\() ->
testRule """module A exposing (b)
import Foo exposing (a)"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Imported variable `a` is not used"
, under = "a"
}
@ -190,16 +190,16 @@ import Foo exposing (a)"""
\() ->
testRule """module A exposing (d)
import Foo exposing (C, a, b)"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Imported variable `b` is not used"
, under = "b"
}
, Lint.Test2.error
, Lint.Test.error
{ message = "Imported variable `a` is not used"
, under = "a"
}
, Lint.Test2.error
, Lint.Test.error
{ message = "Imported type `C` is not used"
, under = "C"
}
@ -212,30 +212,30 @@ import Foo exposing (C, a, b)"""
testRule """module A exposing (a)
a = case thing of
Foo b c -> []"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
-- Should B and C be reported if they are not used? Probably.
, test "should report unused custom type declarations" <|
\() ->
testRule """module A exposing (a)
type A = B | C"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Type `A` is not used"
, under = "A"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 6 }, end = { row = 2, column = 7 } }
|> Lint.Test.atExactly { start = { row = 2, column = 6 }, end = { row = 2, column = 7 } }
]
, test "should report unused type aliases declarations" <|
\() ->
testRule """module A exposing (a)
type alias A = { a : B }"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Type `A` is not used"
, under = "A"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 12 }, end = { row = 2, column = 13 } }
|> Lint.Test.atExactly { start = { row = 2, column = 12 }, end = { row = 2, column = 13 } }
]
, test "should not report type used in a signature" <|
\() ->
@ -243,63 +243,63 @@ type alias A = { a : B }"""
type alias A = { a : B }
a : A
a = {a = 1}"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report type used in a signature with multiple arguments" <|
\() ->
testRule """module A exposing (a)
type alias A = { a : B }
a : String -> A
a str = {a = str}"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report type used in a signature with parameterized types (as generic type)" <|
\() ->
testRule """module A exposing (a)
type alias A = { a : B }
a : A B
a = []"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report type used in a signature with parameterized types (as parameter)" <|
\() ->
testRule """module A exposing (a)
type alias A = { a : B }
a : List A
a = []"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report type used in a signature with a record" <|
\() ->
testRule """module A exposing (a)
type alias A = { a : B }
a : { c: A }
a str = {c = str}"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report type used in a signature with a generic record" <|
\() ->
testRule """module A exposing (a)
type alias A = { a : B }
a : { r | c: A }
a str = {c = str}"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report type if it's exposed" <|
\() ->
testRule """module A exposing (A)
type A a = B a"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report custom type if it's exposed with its sub-types" <|
\() ->
testRule """module A exposing (A(..))
type A = B | C | D"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused variable even if it's present in a generic type" <|
\() ->
testRule """module A exposing (A)
a = 1
type A a = B a"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `a` is not used"
, under = "a"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 2 } }
|> Lint.Test.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 2 } }
]
, test "should report unused variable even if it's present in a generic record type" <|
\() ->
@ -307,19 +307,19 @@ type A a = B a"""
r = 1
a : { r | c: A }
a str = {c = str}"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `r` is not used"
, under = "r"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 2 } }
|> Lint.Test.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 2 } }
]
, test "should report unused operator import" <|
\() ->
testRule """module A exposing (a)
import Parser exposing ((</>))"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Imported operator `</>` is not used"
, under = "(</>)"
}
@ -329,23 +329,23 @@ import Parser exposing ((</>))"""
testRule """module A exposing (a)
import Parser exposing ((</>))
a = 1 </> 2"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report used operator (prefix)" <|
\() ->
testRule """module A exposing (a)
import Parser exposing ((</>))
a = (</>) 2"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused opaque types" <|
\() ->
testRule """module A exposing (a)
type A = A Int"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Type `A` is not used"
, under = "A"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 6 }, end = { row = 2, column = 7 } }
|> Lint.Test.atExactly { start = { row = 2, column = 6 }, end = { row = 2, column = 7 } }
]
, test "should not report used opaque types" <|
\() ->
@ -353,13 +353,13 @@ type A = A Int"""
type A = A Int
a : A
a = 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused import" <|
\() ->
testRule """module A exposing (a)
import Html"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Imported module `Html` is not used"
, under = "Html"
}
@ -368,8 +368,8 @@ import Html"""
\() ->
testRule """module A exposing (a)
import Html.Styled.Attributes"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Imported module `Html.Styled.Attributes` is not used"
, under = "Html.Styled.Attributes"
}
@ -378,41 +378,41 @@ import Html.Styled.Attributes"""
\() ->
testRule """module A exposing (a)
import Html.Styled.Attributes exposing (..)"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused variable even if a homonym from a module is used" <|
\() ->
testRule """module A exposing (a)
href = 1
a = Html.Styled.Attributes.href"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Variable `href` is not used"
, under = "href"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 5 } }
|> Lint.Test.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 5 } }
]
, test "should not report used import (function access)" <|
\() ->
testRule """module A exposing (a)
import Html.Styled.Attributes
a = Html.Styled.Attributes.href"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report unused import if it is aliased" <|
\() ->
testRule """module A exposing (a)
import Html.Styled.Attributes as Html
a = Html.href"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused import alias" <|
\() ->
testRule """module A exposing (a)
import Html.Styled.Attributes as Html"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Module alias `Html` is not used"
, under = "Html"
}
|> Lint.Test2.atExactly { start = { row = 2, column = 34 }, end = { row = 2, column = 38 } }
|> Lint.Test.atExactly { start = { row = 2, column = 34 }, end = { row = 2, column = 38 } }
]
, test "should not report import that exposes a used exposed type" <|
\() ->
@ -420,14 +420,14 @@ import Html.Styled.Attributes as Html"""
import B exposing (C(..))
a : C
a = 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report import that exposes an unused exposed type (but whose subtype is potentially used)" <|
\() ->
testRule """module A exposing (a)
import B exposing (C(..))
a : D
a = 1"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should not report types that are used in ports" <|
\() ->
testRule """module A exposing (output, input)
@ -435,14 +435,14 @@ import Json.Decode
import Json.Encode
port output : Json.Encode.Value -> Cmd msg
port input : (Json.Decode.Value -> msg) -> Sub msg"""
|> Lint.Test2.expectNoErrors
|> Lint.Test.expectNoErrors
, test "should report unused ports (ingoing)" <|
\() ->
testRule """module A exposing (a)
import Json.Decode
port input : (Json.Decode.Value -> msg) -> Sub msg"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Port `input` is not used (Warning: Removing this port may break your application if it is used in the JS code)"
, under = "input"
}
@ -452,8 +452,8 @@ port input : (Json.Decode.Value -> msg) -> Sub msg"""
testRule """module A exposing (a)
import Json.Encode
port output : Json.Encode.Value -> Cmd msg"""
|> Lint.Test2.expectErrors
[ Lint.Test2.error
|> Lint.Test.expectErrors
[ Lint.Test.error
{ message = "Port `output` is not used (Warning: Removing this port may break your application if it is used in the JS code)"
, under = "output"
}