Skip to content

Commit 71fef7c

Browse files
committed
Write to a temp file if there are diffs and output instructions for how to compare
1 parent c9e645c commit 71fef7c

3 files changed

Lines changed: 94 additions & 24 deletions

File tree

src/Elm/Kernel/Test.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function _Test_runThunk(thunk)
2020

2121
const fs = require('node:fs');
2222
const path = require('node:path');
23+
const os = require('node:os');
2324

2425
function _Test_readFile(filePath)
2526
{
@@ -51,18 +52,17 @@ function _Test_readFile(filePath)
5152
}
5253
}
5354

54-
var _Test_writeFile = F2(function(filePath, contents)
55+
function WriteFile(root, filePath, contents)
5556
{
5657
// Test for this early as `resolve` will strip training slashes
5758
if (filePath.slice(-1) == path.sep) {
5859
return __Result_Err(__File_IsDirectory);
5960
}
6061

61-
// Protect against writing files above the "tests" directory
62-
const testsPath = path.resolve("tests");
63-
const fullPath = path.resolve(testsPath, filePath);
62+
// Protect against writing files above the root directory
63+
const fullPath = path.resolve(root, filePath);
6464

65-
if (!fullPath.startsWith(testsPath))
65+
if (!fullPath.startsWith(root))
6666
{
6767
return __Result_Err(__File_PathEscapesDirectory);
6868
}
@@ -74,10 +74,24 @@ var _Test_writeFile = F2(function(filePath, contents)
7474

7575
try {
7676
fs.writeFileSync(fullPath, contents);
77-
return __Result_Ok(__Utils_Tuple0);
77+
return __Result_Ok(fullPath);
7878
}
7979
catch (err)
8080
{
8181
return __Result_Err(__File_GeneralFileError(err.toString()));
8282
}
83+
}
84+
85+
var _Test_writeFile = F2(function(filePath, contents)
86+
{
87+
return WriteFile(path.resolve("tests"), filePath, contents);
88+
})
89+
90+
var tempDir = null;
91+
var _Test_writeTempFile = F2(function(filePath, contents)
92+
{
93+
if (tempDir === null)
94+
tempDir = os.tmpdir();
95+
96+
return WriteFile(tempDir, filePath, contents);
8397
})

src/Expect.elm

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ module Expect exposing
33
, lessThan, atMost, greaterThan, atLeast
44
, FloatingPointTolerance(..), within, notWithin
55
, ok, err, equalLists, equalDicts, equalSets
6-
, pass, fail, onFail, equalToFile
6+
, equalToFile
7+
, pass, fail, onFail
78
)
89

910
{-| A library to create `Expectation`s, which describe a claim to be tested.
@@ -42,10 +43,12 @@ or both. For an in-depth look, see our [Guide to Floating Point Comparison](#gui
4243
4344
@docs ok, err, equalLists, equalDicts, equalSets
4445
46+
4547
## Golden Files
4648
4749
@docs equalToFile
4850
51+
4952
## Customizing
5053
5154
These functions will let you build your own expectations.
@@ -106,8 +109,8 @@ Another example is comparing values that are on either side of zero. `0.0001` is
106109
-}
107110

108111
import Dict exposing (Dict)
109-
import Set exposing (Set)
110112
import File
113+
import Set exposing (Set)
111114
import Test.Distribution
112115
import Test.Expectation
113116
import Test.Internal as Internal
@@ -580,7 +583,7 @@ equalSets expected actual =
580583
reportCollectionFailure "Expect.equalSets" expected actual missingKeys extraKeys
581584

582585

583-
{-| Tests the a String is equal to the contents of the file stored at the file path.
586+
{-| Tests the a String is equal to the contents of the file stored at the file path.
584587
585588
If the file does not exist, it will be created and this test will pass.
586589
@@ -591,30 +594,53 @@ All file paths are scoped to be within the "tests/" directory.
591594
-}
592595
equalToFile : String -> String -> Expectation
593596
equalToFile filePath actual =
594-
case File.readFile filePath of
597+
case File.readFile filePath of
595598
Err File.FileNotFound ->
596-
case File.writeFile filePath actual of
597-
Err (File.GeneralFileError fileError) ->
599+
case File.writeFile filePath actual of
600+
Err (File.GeneralFileError fileError) ->
598601
Test.Expectation.fail { description = "Expect.equalToFile encountered a general file error: " ++ fileError, reason = Custom }
599602

600603
-- This case should be impossible non general file errors should have been surfaced in the call to `readFile` above.
601-
Err _ ->
602-
Test.Expectation.fail { description = "Expect.equalToFile encountered an unexpected error", reason = Custom }
604+
Err _ ->
605+
Test.Expectation.fail { description = "Expect.equalToFile encountered an unexpected error", reason = Custom }
603606

604607
Ok _ ->
605608
pass
606609

607-
Err File.IsDirectory ->
610+
Err File.IsDirectory ->
608611
Test.Expectation.fail { description = "Expect.equalToFile was given a directory instead of a file", reason = Custom }
609612

610-
Err File.PathEscapesDirectory ->
613+
Err File.PathEscapesDirectory ->
611614
Test.Expectation.fail { description = "Expect.equalToFile was given a path that would escape the tests/ directory", reason = Custom }
612615

613-
Err (File.GeneralFileError fileError) ->
616+
Err (File.GeneralFileError fileError) ->
614617
Test.Expectation.fail { description = "Expect.equalToFile encountered a general file error: " ++ fileError, reason = Custom }
615618

616-
Ok contents ->
617-
equateWith ("equalToFile \'" ++ filePath ++ "\'") (==) contents actual
619+
Ok ( existingAbsolutePath, contents ) ->
620+
if actual == contents then
621+
pass
622+
623+
else
624+
case File.writeTempFile filePath actual of
625+
Ok newAbsolutePath ->
626+
let
627+
message =
628+
[ "The contents of " ++ filePath ++ "changed!"
629+
, "To compare run: git diff --no-index " ++ existingAbsolutePath ++ " " ++ newAbsolutePath
630+
]
631+
632+
messageWithVisualDiff =
633+
if String.endsWith ".html" filePath then
634+
message ++ [ "To visually compare run: open file://" ++ existingAbsolutePath ++ " file://" ++ newAbsolutePath ]
635+
636+
else
637+
message
638+
in
639+
Test.Expectation.fail { description = String.join "\n\n" messageWithVisualDiff, reason = Custom }
640+
641+
_ ->
642+
Test.Expectation.fail { description = "Expect.equalToFile encountered an unexpected error", reason = Custom }
643+
618644

619645
{-| Always passes.
620646

src/File.elm

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
1-
module File exposing (readFile, writeFile, FileError(..))
1+
module File exposing (AbsolutePath, FileError(..), RelativePath, readFile, writeFile, writeTempFile)
22

33
import Elm.Kernel.Test
44

5+
56
type FileError
67
= FileNotFound
78
| IsDirectory
89
| PathEscapesDirectory
910
| GeneralFileError String
1011

11-
readFile : String -> Result FileError String
12-
readFile = Elm.Kernel.Test.readFile
1312

14-
writeFile : String -> String -> Result FileError ()
15-
writeFile = Elm.Kernel.Test.writeFile
13+
type alias RelativePath =
14+
String
15+
16+
17+
type alias AbsolutePath =
18+
String
19+
20+
21+
{-| Read the contents of the filePath relative to "tests/"
22+
-}
23+
readFile : RelativePath -> Result FileError ( AbsolutePath, String )
24+
readFile =
25+
Elm.Kernel.Test.readFile
26+
27+
28+
{-| Write the contents of the second argument to the file path in the first argument relative to "tests/"
29+
30+
Returns the absolute file path if successful.
31+
32+
-}
33+
writeFile : RelativePath -> String -> Result FileError AbsolutePath
34+
writeFile =
35+
Elm.Kernel.Test.writeFile
36+
37+
38+
{-| Write the contents of the second argument to the file path in the first argument relative to a temp directory
39+
40+
Returns the absolute file path if successful.
41+
42+
-}
43+
writeTempFile : RelativePath -> String -> Result FileError AbsolutePath
44+
writeTempFile =
45+
Elm.Kernel.Test.writeTempFile

0 commit comments

Comments
 (0)