mirror of
https://github.com/jfmengels/elm-review.git
synced 2024-12-25 02:34:36 +03:00
Add a way to check in tests that the fixed code is correct
This commit is contained in:
parent
b973e90936
commit
919467fee6
@ -1081,6 +1081,10 @@ error { message, details } range =
|
||||
|
||||
Take a look at [`Lint.Fix`](./Lint-Fix) to know more on how to makes fixes.
|
||||
|
||||
If you pass `withFixes` an empty list, the error will be considered as having no
|
||||
automatic fix available. ALso, calling `withFixes` several times on an error will
|
||||
overwrite the previous fixes.
|
||||
|
||||
**Note**: Each fix applies on a location in the code, defined by a range. To avoid an
|
||||
unpredictable result, those ranges may not overlap. The order of the fixes does
|
||||
not matter.
|
||||
|
@ -1,6 +1,6 @@
|
||||
module Lint.Test exposing
|
||||
( LintResult, run
|
||||
, ExpectedError, expectErrors, expectNoErrors, error, atExactly
|
||||
, ExpectedError, expectErrors, expectNoErrors, error, atExactly, whenFixed
|
||||
)
|
||||
|
||||
{-| Module that helps you test your linting rules, using [`elm-test`](https://package.elm-lang.org/packages/elm-explorations/test/latest).
|
||||
@ -78,7 +78,7 @@ changes drastically.
|
||||
|
||||
# Making assertions
|
||||
|
||||
@docs ExpectedError, expectErrors, expectNoErrors, error, atExactly
|
||||
@docs ExpectedError, expectErrors, expectNoErrors, error, atExactly, whenFixed
|
||||
|
||||
-}
|
||||
|
||||
@ -86,6 +86,7 @@ import Array exposing (Array)
|
||||
import Elm.Syntax.Range exposing (Range)
|
||||
import Expect exposing (Expectation)
|
||||
import Lint exposing (lintSource)
|
||||
import Lint.Fix as Fix
|
||||
import Lint.Rule as Rule exposing (Error, Rule)
|
||||
import Lint.Test.ErrorMessage as ErrorMessage
|
||||
|
||||
@ -98,7 +99,8 @@ type LintResult
|
||||
|
||||
|
||||
type alias CodeInspector =
|
||||
{ getCodeAtLocation : Range -> Maybe String
|
||||
{ source : String
|
||||
, getCodeAtLocation : Range -> Maybe String
|
||||
, checkIfLocationIsAmbiguous : Error -> String -> Expectation
|
||||
}
|
||||
|
||||
@ -110,6 +112,7 @@ type ExpectedError
|
||||
{ message : String
|
||||
, details : List String
|
||||
, under : Under
|
||||
, fixedSource : Maybe String
|
||||
}
|
||||
|
||||
|
||||
@ -154,9 +157,11 @@ run rule source =
|
||||
, details = Lint.errorDetails error_
|
||||
}
|
||||
(Lint.errorRange error_)
|
||||
|> Rule.withFixes (Lint.fixes error_ |> Maybe.withDefault [])
|
||||
)
|
||||
|> SuccessfulRun
|
||||
{ getCodeAtLocation = getCodeAtLocationInSourceCode source
|
||||
{ source = source
|
||||
, getCodeAtLocation = getCodeAtLocationInSourceCode source
|
||||
, checkIfLocationIsAmbiguous = checkIfLocationIsAmbiguousInSourceCode source
|
||||
}
|
||||
|
||||
@ -274,6 +279,7 @@ error input =
|
||||
{ message = input.message
|
||||
, details = input.details
|
||||
, under = Under input.under
|
||||
, fixedSource = Nothing
|
||||
}
|
||||
|
||||
|
||||
@ -315,6 +321,11 @@ atExactly range ((ExpectedError expectedError_) as expectedError) =
|
||||
ExpectedError { expectedError_ | under = UnderExactly (getUnder expectedError) range }
|
||||
|
||||
|
||||
whenFixed : String -> ExpectedError -> ExpectedError
|
||||
whenFixed fixedSource ((ExpectedError expectedError_) as expectedError) =
|
||||
ExpectedError { expectedError_ | fixedSource = Just fixedSource }
|
||||
|
||||
|
||||
getUnder : ExpectedError -> String
|
||||
getUnder (ExpectedError expectedError) =
|
||||
case expectedError.under of
|
||||
@ -413,6 +424,7 @@ checkErrorMatch codeInspector ((ExpectedError expectedError_) as expectedError)
|
||||
|> always
|
||||
, checkMessageAppearsUnder codeInspector error_ expectedError
|
||||
, checkDetailsAreCorrect error_ expectedError
|
||||
, always <| checkFixesAreCorrect codeInspector error_ expectedError
|
||||
]
|
||||
|
||||
|
||||
@ -457,6 +469,30 @@ checkDetailsAreCorrect error_ (ExpectedError expectedError) =
|
||||
]
|
||||
|
||||
|
||||
checkFixesAreCorrect : CodeInspector -> Error -> ExpectedError -> Expectation
|
||||
checkFixesAreCorrect codeInspector error_ ((ExpectedError expectedError_) as expectedError) =
|
||||
case ( expectedError_.fixedSource, Rule.errorFixes error_ ) of
|
||||
( Nothing, Nothing ) ->
|
||||
Expect.pass
|
||||
|
||||
( Just expectedFixedSource, Nothing ) ->
|
||||
ErrorMessage.missingFixes (extractExpectedErrorData expectedError)
|
||||
|> Expect.fail
|
||||
|
||||
( Nothing, Just fixes ) ->
|
||||
ErrorMessage.unexpectedFixes error_
|
||||
|> Expect.fail
|
||||
|
||||
( Just expectedFixedSource, Just fixes ) ->
|
||||
case Fix.fix fixes codeInspector.source of
|
||||
Fix.Successful fixedSource ->
|
||||
(fixedSource == expectedFixedSource)
|
||||
|> Expect.true (ErrorMessage.fixedCodeMismatch fixedSource expectedFixedSource error_)
|
||||
|
||||
Fix.Errored _ ->
|
||||
Expect.fail "Supplied fixes caused a problem"
|
||||
|
||||
|
||||
extractExpectedErrorData : ExpectedError -> ErrorMessage.ExpectedErrorData
|
||||
extractExpectedErrorData ((ExpectedError expectedErrorContent) as expectedError) =
|
||||
{ message = expectedErrorContent.message
|
||||
|
@ -2,6 +2,7 @@ module Lint.Test.ErrorMessage exposing
|
||||
( ExpectedErrorData
|
||||
, parsingFailure, messageMismatch, emptyDetails, unexpectedDetails, wrongLocation, didNotExpectErrors
|
||||
, underMismatch, expectedMoreErrors, tooManyErrors, locationIsAmbiguousInSourceCode
|
||||
, missingFixes, unexpectedFixes, fixedCodeMismatch
|
||||
, impossibleState
|
||||
)
|
||||
|
||||
@ -13,6 +14,7 @@ module Lint.Test.ErrorMessage exposing
|
||||
@docs ExpectedErrorData
|
||||
@docs parsingFailure, messageMismatch, emptyDetails, unexpectedDetails, wrongLocation, didNotExpectErrors
|
||||
@docs underMismatch, expectedMoreErrors, tooManyErrors, locationIsAmbiguousInSourceCode
|
||||
@docs missingFixes, unexpectedFixes, fixedCodeMismatch
|
||||
@docs impossibleState
|
||||
|
||||
-}
|
||||
@ -220,6 +222,53 @@ impossibleState =
|
||||
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."""
|
||||
|
||||
|
||||
missingFixes : ExpectedErrorData -> String
|
||||
missingFixes expectedError =
|
||||
"""MISSING FIXES
|
||||
|
||||
I expected that the error with the following message
|
||||
|
||||
""" ++ wrapInQuotes expectedError.message ++ """
|
||||
|
||||
would provide some fixes, but I didn't find any.
|
||||
|
||||
Hint: It's probable that you either forgot to call `Rule.withFixes` on the
|
||||
error that you created, or that the list of provided fixes was empty."""
|
||||
|
||||
|
||||
unexpectedFixes : Error -> String
|
||||
unexpectedFixes error =
|
||||
"""UNEXPECTED FIXES
|
||||
|
||||
I expected that the error with the following message
|
||||
|
||||
""" ++ wrapInQuotes (Rule.errorMessage error) ++ """
|
||||
|
||||
would not have any fixes, but it provided some.
|
||||
|
||||
Hint: You may have forgotten to call `Lint.Test.whenFixed`
|
||||
It's probable that you either forgot to call `Rule.withFixes` on the
|
||||
error that you created, or that the list of provided fixes was empty."""
|
||||
|
||||
|
||||
fixedCodeMismatch : SourceCode -> SourceCode -> Error -> String
|
||||
fixedCodeMismatch resultingSourceCode expectedSourceCode error =
|
||||
"""FIXED CODE MISMATCH
|
||||
|
||||
I found a different fixed source code than expected for the error with the
|
||||
following message:
|
||||
|
||||
""" ++ wrapInQuotes (Rule.errorMessage error) ++ """
|
||||
|
||||
I found the following result after the fixes have been applied:
|
||||
|
||||
""" ++ formatSourceCode resultingSourceCode ++ """
|
||||
|
||||
but I was expecting:
|
||||
|
||||
""" ++ formatSourceCode expectedSourceCode
|
||||
|
||||
|
||||
|
||||
-- STYLIZING AND FORMATTING
|
||||
|
||||
@ -235,7 +284,14 @@ formatSourceCode string =
|
||||
|
||||
else
|
||||
lines
|
||||
|> List.map (\str -> " " ++ str)
|
||||
|> List.map
|
||||
(\str ->
|
||||
if str == "" then
|
||||
""
|
||||
|
||||
else
|
||||
" " ++ str
|
||||
)
|
||||
|> String.join "\n"
|
||||
|> (\str -> "```\n" ++ str ++ "\n ```")
|
||||
|
||||
|
@ -2,6 +2,7 @@ module ErrorMessageTest exposing (all)
|
||||
|
||||
import Elm.Syntax.Range exposing (Range)
|
||||
import Expect exposing (Expectation)
|
||||
import Lint.Fix as Fix
|
||||
import Lint.Rule as Rule exposing (Error)
|
||||
import Lint.Test.ErrorMessage as ErrorMessage exposing (ExpectedErrorData)
|
||||
import Test exposing (Test, describe, test)
|
||||
@ -20,6 +21,9 @@ all =
|
||||
, expectedMoreErrorsTest
|
||||
, tooManyErrorsTest
|
||||
, locationIsAmbiguousInSourceCodeTest
|
||||
, missingFixesTest
|
||||
, unexpectedFixesTest
|
||||
, fixedCodeMismatchTest
|
||||
]
|
||||
|
||||
|
||||
@ -580,6 +584,119 @@ Tip: I found them at:
|
||||
]
|
||||
|
||||
|
||||
missingFixesTest : Test
|
||||
missingFixesTest =
|
||||
test "missingFixes" <|
|
||||
\() ->
|
||||
let
|
||||
expectedError : ExpectedErrorData
|
||||
expectedError =
|
||||
{ message = "Some error"
|
||||
, details = [ "Some details" ]
|
||||
, under = "Debug.log"
|
||||
}
|
||||
in
|
||||
ErrorMessage.missingFixes expectedError
|
||||
|> expectMessageEqual """
|
||||
MISSING FIXES
|
||||
|
||||
I expected that the error with the following message
|
||||
|
||||
`Some error`
|
||||
|
||||
would provide some fixes, but I didn't find any.
|
||||
|
||||
Hint: It's probable that you either forgot to call `Rule.withFixes` on the
|
||||
error that you created, or that the list of provided fixes was empty."""
|
||||
|
||||
|
||||
unexpectedFixesTest : Test
|
||||
unexpectedFixesTest =
|
||||
test "unexpectedFixes" <|
|
||||
\() ->
|
||||
let
|
||||
range : Range
|
||||
range =
|
||||
{ start = { row = 3, column = 1 }, end = { row = 4, column = 3 } }
|
||||
|
||||
error : Error
|
||||
error =
|
||||
Rule.error
|
||||
{ message = "Some error"
|
||||
, details = [ "Some details" ]
|
||||
}
|
||||
range
|
||||
|> Rule.withFixes [ Fix.removeRange range ]
|
||||
in
|
||||
ErrorMessage.unexpectedFixes error
|
||||
|> expectMessageEqual """
|
||||
UNEXPECTED FIXES
|
||||
|
||||
I expected that the error with the following message
|
||||
|
||||
`Some error`
|
||||
|
||||
would not have any fixes, but it provided some.
|
||||
|
||||
Hint: You may have forgotten to call `Lint.Test.whenFixed`
|
||||
It's probable that you either forgot to call `Rule.withFixes` on the
|
||||
error that you created, or that the list of provided fixes was empty."""
|
||||
|
||||
|
||||
fixedCodeMismatchTest : Test
|
||||
fixedCodeMismatchTest =
|
||||
test "fixedCodeMismatch" <|
|
||||
\() ->
|
||||
let
|
||||
sourceCode : String
|
||||
sourceCode =
|
||||
"""module A exposing (b)
|
||||
abcd =
|
||||
1"""
|
||||
|
||||
expectedSourceCode : String
|
||||
expectedSourceCode =
|
||||
"""module A exposing (b)
|
||||
abcd =
|
||||
2"""
|
||||
|
||||
error : Error
|
||||
error =
|
||||
Rule.error
|
||||
{ message = "Some error"
|
||||
, details = [ "Some details" ]
|
||||
}
|
||||
{ start = { row = 3, column = 1 }, end = { row = 3, column = 5 } }
|
||||
in
|
||||
ErrorMessage.fixedCodeMismatch
|
||||
sourceCode
|
||||
expectedSourceCode
|
||||
error
|
||||
|> expectMessageEqual """
|
||||
FIXED CODE MISMATCH
|
||||
|
||||
I found a different fixed source code than expected for the error with the
|
||||
following message:
|
||||
|
||||
`Some error`
|
||||
|
||||
I found the following result after the fixes have been applied:
|
||||
|
||||
```
|
||||
module A exposing (b)
|
||||
abcd =
|
||||
1
|
||||
```
|
||||
|
||||
but I was expecting:
|
||||
|
||||
```
|
||||
module A exposing (b)
|
||||
abcd =
|
||||
2
|
||||
```"""
|
||||
|
||||
|
||||
dummyRange : Range
|
||||
dummyRange =
|
||||
{ start = { row = 2, column = 1 }, end = { row = 2, column = 5 } }
|
||||
|
@ -48,19 +48,41 @@ b = a 1"""
|
||||
, test "should report unused top-level variables" <|
|
||||
\() ->
|
||||
testRule """module SomeModule exposing (b)
|
||||
a = 1"""
|
||||
b = 1
|
||||
a = 2"""
|
||||
|> Lint.Test.expectErrors
|
||||
[ Lint.Test.error
|
||||
{ message = "Variable `a` is not used"
|
||||
, details = details
|
||||
, under = "a"
|
||||
}
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (b)
|
||||
b = 1
|
||||
"""
|
||||
]
|
||||
, test "should report unused top-level variables with type annotation" <|
|
||||
\() ->
|
||||
testRule """module SomeModule exposing (b)
|
||||
b = 1
|
||||
a : Int
|
||||
a = 2"""
|
||||
|> Lint.Test.expectErrors
|
||||
[ Lint.Test.error
|
||||
{ message = "Variable `a` is not used"
|
||||
, details = details
|
||||
, under = "a"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 4, column = 1 }, end = { row = 4, column = 2 } }
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (b)
|
||||
b = 1
|
||||
"""
|
||||
]
|
||||
, test "should report unused top-level variables even if they are annotated" <|
|
||||
\() ->
|
||||
testRule """module SomeModule exposing (b)
|
||||
a: Int
|
||||
a = 1"""
|
||||
a = 1
|
||||
b = 2"""
|
||||
|> Lint.Test.expectErrors
|
||||
[ Lint.Test.error
|
||||
{ message = "Variable `a` is not used"
|
||||
@ -68,6 +90,9 @@ a = 1"""
|
||||
, under = "a"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 3, column = 1 }, end = { row = 3, column = 2 } }
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (b)
|
||||
|
||||
b = 2"""
|
||||
]
|
||||
, test "should not report unused top-level variables if everything is exposed" <|
|
||||
\() ->
|
||||
@ -93,6 +118,10 @@ c = 3"""
|
||||
, details = details
|
||||
, under = "c"
|
||||
}
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (a, b)
|
||||
a = 1
|
||||
b = 2
|
||||
"""
|
||||
]
|
||||
, test "should not report unused top-level variables if everything is exposed (port module)" <|
|
||||
\() ->
|
||||
@ -118,6 +147,10 @@ c = 3"""
|
||||
, details = details
|
||||
, under = "c"
|
||||
}
|
||||
|> Lint.Test.whenFixed """port module SomeModule exposing (a, b)
|
||||
a = 1
|
||||
b = 2
|
||||
"""
|
||||
]
|
||||
, test "should report unused variable even if a homonym from a module is used" <|
|
||||
\() ->
|
||||
@ -131,6 +164,9 @@ a = Html.Styled.Attributes.href"""
|
||||
, under = "href"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 5 } }
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (a)
|
||||
|
||||
a = Html.Styled.Attributes.href"""
|
||||
]
|
||||
]
|
||||
|
||||
@ -148,6 +184,7 @@ a = let b = 1
|
||||
, details = details
|
||||
, under = "b"
|
||||
}
|
||||
|> Lint.Test.whenFixed "module SomeModule exposing (a)\na = let \n in 2"
|
||||
]
|
||||
, test "should report unused variables from let even if they are exposed by name" <|
|
||||
\() ->
|
||||
@ -161,6 +198,7 @@ a = let b = 1
|
||||
, under = "b"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 2, column = 9 }, end = { row = 2, column = 10 } }
|
||||
|> Lint.Test.whenFixed "module SomeModule exposing (a, b)\na = let \n in 2"
|
||||
]
|
||||
, test "should report unused functions from let even if they are exposed by name" <|
|
||||
\() ->
|
||||
@ -174,6 +212,7 @@ a = let b param = 1
|
||||
, under = "b"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 2, column = 9 }, end = { row = 2, column = 10 } }
|
||||
|> Lint.Test.whenFixed "module SomeModule exposing (a, b)\na = let \n in 2"
|
||||
]
|
||||
, test "should report unused variables from let even if everything is exposed" <|
|
||||
\() ->
|
||||
@ -186,6 +225,7 @@ a = let b = 1
|
||||
, details = details
|
||||
, under = "b"
|
||||
}
|
||||
|> Lint.Test.whenFixed "module SomeModule exposing (..)\na = let \n in 2"
|
||||
]
|
||||
, test "should not report variables from let declarations that are used in the expression" <|
|
||||
\() ->
|
||||
@ -257,6 +297,10 @@ a = { b | c = 3 }"""
|
||||
, under = "c"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 3, column = 1 }, end = { row = 3, column = 2 } }
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (a)
|
||||
b = { z = 1, c = 2 }
|
||||
|
||||
a = { b | c = 3 }"""
|
||||
]
|
||||
]
|
||||
|
||||
@ -675,6 +719,9 @@ type A a = B a"""
|
||||
, under = "a"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 2 } }
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (A)
|
||||
|
||||
type A a = B a"""
|
||||
]
|
||||
, test "should report unused variable even if it is present in a generic record type" <|
|
||||
\() ->
|
||||
@ -689,6 +736,10 @@ a str = {c = str}"""
|
||||
, under = "r"
|
||||
}
|
||||
|> Lint.Test.atExactly { start = { row = 2, column = 1 }, end = { row = 2, column = 2 } }
|
||||
|> Lint.Test.whenFixed """module SomeModule exposing (a)
|
||||
|
||||
a : { r | c: A }
|
||||
a str = {c = str}"""
|
||||
]
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user