Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion src/Elm/Kernel/Test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*

import Elm.Kernel.Utils exposing (Tuple0)
import File exposing (FileNotFound, GeneralFileError, IsDirectory, PathEscapesDirectory)
import Result exposing (Err, Ok)

*/


function _Test_runThunk(thunk)
{
try {
Expand All @@ -16,3 +16,71 @@ function _Test_runThunk(thunk)
return __Result_Err(err.toString());
}
}


const fs = require('fs');
const path = require('path');

function _Test_readFile(filePath)
{
// Test for this early as `resolve` will strip training slashes
if (filePath.slice(-1) == path.sep) {
return __Result_Err(__File_IsDirectory);
}

// Protect against reading files above the "tests" directory
const testsPath = path.resolve("tests");
const fullPath = path.resolve(testsPath, filePath);

if (!fullPath.startsWith(testsPath))
{
return __Result_Err(__File_PathEscapesDirectory);
}

try {
return __Result_Ok(fs.readFileSync(fullPath, { encoding: 'utf8' }));
}
catch (err)
{
if (err.code == "ENOENT"){
return __Result_Err(__File_FileNotFound);
}
else {
return __Result_Err(__File_GeneralFileError(err.toString()));
}
}
}

var _Test_writeFile = F2(function(filePath, contents)
{
// Test for this early as `resolve` will strip training slashes
if (filePath.slice(-1) == path.sep) {
return __Result_Err(__File_IsDirectory);
}

// Protect against writing files above the "tests" directory
const testsPath = path.resolve("tests");
const fullPath = path.resolve(testsPath, filePath);

if (!fullPath.startsWith(testsPath))
{
return __Result_Err(__File_PathEscapesDirectory);
}

const fullDir = path.dirname(fullPath);

if (!fs.existsSync(fullDir))
{
// Can this make a nested directory?
fs.mkdirSync(fullDir, {recursive: true});
}

try {
fs.writeFileSync(fullPath, contents);
return __Result_Ok(__Utils_Tuple0);
}
catch (err)
{
return __Result_Err(__File_GeneralFileError(err.toString()));
}
})
42 changes: 41 additions & 1 deletion src/Expect.elm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Expect exposing
, lessThan, atMost, greaterThan, atLeast
, FloatingPointTolerance(..), within, notWithin
, ok, err, equalLists, equalDicts, equalSets
, pass, fail, onFail
, pass, fail, onFail, equalToFile
)

{-| A library to create `Expectation`s, which describe a claim to be tested.
Expand Down Expand Up @@ -42,6 +42,9 @@ or both. For an in-depth look, see our [Guide to Floating Point Comparison](#gui

@docs ok, err, equalLists, equalDicts, equalSets

## Golden Files

@docs equalToFile

## Customizing

Expand Down Expand Up @@ -104,6 +107,7 @@ Another example is comparing values that are on either side of zero. `0.0001` is

import Dict exposing (Dict)
import Set exposing (Set)
import File
import Test.Distribution
import Test.Expectation
import Test.Internal as Internal
Expand Down Expand Up @@ -576,6 +580,42 @@ equalSets expected actual =
reportCollectionFailure "Expect.equalSets" expected actual missingKeys extraKeys


{-| Tests the a String is equal to the contents of the file stored at the file path.

If the file does not exist, it will be created and this test will pass.

If the file does exist, then this test will pass if its contents are equal to the actual string.

All file paths are scoped to be within the "tests/" directory.

-}
equalToFile : String -> String -> Expectation
equalToFile filePath actual =
case File.readFile filePath of
Err File.FileNotFound ->
case File.writeFile filePath actual of
Err (File.GeneralFileError fileError) ->
Test.Expectation.fail { description = "Expect.equalToFile encountered a general file error: " ++ fileError, reason = Custom }

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

Ok _ ->
pass

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

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

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

Ok contents ->
equateWith ("equalToFile \'" ++ filePath ++ "\'") (==) contents actual

{-| Always passes.

import Json.Decode exposing (decodeString, int)
Expand Down
15 changes: 15 additions & 0 deletions src/File.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module File exposing (readFile, writeFile, FileError(..))

import Elm.Kernel.Test

type FileError
= FileNotFound
| IsDirectory
| PathEscapesDirectory
| GeneralFileError String

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

writeFile : String -> String -> Result FileError ()
writeFile = Elm.Kernel.Test.writeFile
18 changes: 18 additions & 0 deletions src/Test/Html/Query.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Test.Html.Query exposing
( Single, Multiple, fromHtml
, find, findAll, children, first, index, keep
, count, contains, has, hasNot, each
, prettyPrintSingle
)

{-| Querying HTML structure.
Expand All @@ -18,6 +19,10 @@ module Test.Html.Query exposing

@docs count, contains, has, hasNot, each

## Debugging

@docs prettyPrintSingle

-}

import Expect exposing (Expectation)
Expand Down Expand Up @@ -496,3 +501,16 @@ each : (Single msg -> Expectation) -> Multiple msg -> Expectation
each check (Internal.Multiple showTrace query) =
Internal.expectAll check query
|> failWithQuery showTrace "Query.each" query

{-| Pretty prints the result of a query as HTML if successful -}
prettyPrintSingle : Single msg -> Result String String
prettyPrintSingle (Internal.Single _ query) =
case Internal.traverse query of
Ok [ element ] ->
Ok <| Internal.prettyPrint element

Ok results ->
Err <| "Query.prettyPrintSingle expected exactly one result from query, but found " ++ String.fromInt (List.length results)

Err queryError ->
Err <| "Query.prettyPrintSingle " ++ Internal.queryErrorToString queryError