mirror of
https://github.com/Smaug123/WoofWare.Expect
synced 2025-10-06 13:08:39 +00:00
Compare commits
6 Commits
WoofWare.E
...
WoofWare.E
Author | SHA1 | Date | |
---|---|---|---|
|
ca74c4816b | ||
|
75899d5668 | ||
|
34a2b460b9 | ||
|
0b64d3dd34 | ||
|
457d7b16de | ||
|
d115185525 |
22
.envrc
22
.envrc
@@ -1 +1,23 @@
|
||||
use flake
|
||||
DOTNET_PATH=$(readlink "$(which dotnet)")
|
||||
SETTINGS_FILE=$(find . -maxdepth 1 -type f -name '*.sln.DotSettings.user')
|
||||
MSBUILD=$(realpath "$(find "$(dirname "$DOTNET_PATH")/../share/dotnet/sdk" -maxdepth 2 -type f -name MSBuild.dll)")
|
||||
if [ -f "$SETTINGS_FILE" ] ; then
|
||||
xmlstarlet ed --inplace \
|
||||
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
|
||||
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
|
||||
-N s="clr-namespace:System;assembly=mscorlib" \
|
||||
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
|
||||
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" \
|
||||
--value "$(realpath "$(dirname "$DOTNET_PATH")/../share/dotnet/dotnet")" \
|
||||
"$SETTINGS_FILE"
|
||||
|
||||
xmlstarlet ed --inplace \
|
||||
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
|
||||
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
|
||||
-N s="clr-namespace:System;assembly=mscorlib" \
|
||||
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
|
||||
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" \
|
||||
--value "$MSBUILD" \
|
||||
"$SETTINGS_FILE"
|
||||
fi
|
||||
|
@@ -17,7 +17,7 @@ An [expect-testing](https://blog.janestreet.com/the-joy-of-expect-tests/) librar
|
||||
|
||||
The basic mechanism works.
|
||||
Snapshot updating is vibe-coded with Opus 4 and is purely text-based; I didn't want to use the F# compiler services because that's a pretty heavyweight dependency which should be confined to a separate test runner entity.
|
||||
It's not very well tested, and I expect it to be kind of brittle.
|
||||
It's fairly well tested, but you will certainly be able to find ways to break it; try not to be too fancy with your syntax around the `snapshot` statement.
|
||||
|
||||
# How to use
|
||||
|
||||
@@ -44,6 +44,13 @@ let ``This test fails: plain text comparison of ToString`` () =
|
||||
snapshot " 123 "
|
||||
return 123
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``With return! and snapshotThrows, you can see exceptions too`` () =
|
||||
expect {
|
||||
snapshotThrows @"System.Exception: oh no"
|
||||
return! (fun () -> failwith<int> "oh no")
|
||||
}
|
||||
```
|
||||
|
||||
You can adjust the formatting:
|
||||
|
44
WoofWare.Expect.Test/SyntaxCases/CommentsAndSpacing.fs
Normal file
44
WoofWare.Expect.Test/SyntaxCases/CommentsAndSpacing.fs
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let multipleComments () =
|
||||
expect {
|
||||
snapshot (* first comment *) (* second comment *)
|
||||
(* third comment on new line *)
|
||||
@"test with many comments"
|
||||
|
||||
return 123
|
||||
}
|
||||
|
||||
let nestedComments () =
|
||||
expect {
|
||||
snapshot (* outer (* inner *) comment *) """nested comment test"""
|
||||
return "nested"
|
||||
}
|
||||
|
||||
let commentWithSpecialChars () =
|
||||
expect {
|
||||
snapshot (* comment with "quotes" and \ backslash *) "regular string"
|
||||
return "special"
|
||||
}
|
||||
|
||||
let lotsOfWhitespace () =
|
||||
expect {
|
||||
snapshot
|
||||
|
||||
|
||||
"string after whitespace"
|
||||
|
||||
return "whitespace"
|
||||
}
|
||||
|
||||
let mixedWhitespaceAndComments () =
|
||||
expect {
|
||||
snapshotJson (* comment 1 *)
|
||||
(* comment 2 *)
|
||||
(* comment 3 *) @"123"
|
||||
|
||||
return 123
|
||||
}
|
69
WoofWare.Expect.Test/SyntaxCases/EdgeCases.fs
Normal file
69
WoofWare.Expect.Test/SyntaxCases/EdgeCases.fs
Normal file
@@ -0,0 +1,69 @@
|
||||
namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot ""
|
||||
return ""
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""
|
||||
return ""
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""
|
||||
return ""
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot " \t\n "
|
||||
return "whitespace"
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @"He said ""Hello"" and she said ""Hi"""
|
||||
return "quotes"
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @"C:\Users\Test\Documents\file.txt"
|
||||
return "path"
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@"This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings"
|
||||
|
||||
return "long line"
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
"""
|
||||
|
||||
Starts with newlines"""
|
||||
|
||||
return "leading"
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
"""Ends with newlines
|
||||
|
||||
|
||||
"""
|
||||
|
||||
return "trailing"
|
||||
}
|
84
WoofWare.Expect.Test/SyntaxCases/MultilineComplex.fs
Normal file
84
WoofWare.Expect.Test/SyntaxCases/MultilineComplex.fs
Normal file
@@ -0,0 +1,84 @@
|
||||
namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let veryLongMultiline () =
|
||||
expect {
|
||||
snapshot
|
||||
"""Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
Line 4
|
||||
Line 5
|
||||
Line 6
|
||||
Line 7
|
||||
Line 8
|
||||
Line 9
|
||||
Line 10
|
||||
Indented line 11
|
||||
More indented line 12
|
||||
Line 13
|
||||
Line 14
|
||||
Line 15"""
|
||||
|
||||
return "long"
|
||||
}
|
||||
|
||||
let multilineWithEmptyLines () =
|
||||
expect {
|
||||
snapshot
|
||||
@"First line
|
||||
|
||||
Third line
|
||||
|
||||
|
||||
Sixth line"
|
||||
|
||||
return "empty lines"
|
||||
}
|
||||
|
||||
let multilineWithSpecialChars () =
|
||||
expect {
|
||||
snapshot
|
||||
"""Special chars:
|
||||
Tab: here
|
||||
Quotes: "double" and 'single'
|
||||
Backslash: \ and \\
|
||||
Unicode: 🎯
|
||||
Regex: .*+?[]"""
|
||||
|
||||
return "special"
|
||||
}
|
||||
|
||||
let multilineJson () =
|
||||
expect {
|
||||
snapshotJson
|
||||
@"{
|
||||
""name"": ""test"",
|
||||
""values"": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
""nested"": {
|
||||
""deep"": true
|
||||
}
|
||||
}"
|
||||
|
||||
return
|
||||
{
|
||||
name = "test"
|
||||
values = [ 1 ; 2 ; 3 ]
|
||||
nested =
|
||||
{|
|
||||
deep = true
|
||||
|}
|
||||
}
|
||||
}
|
||||
|
||||
let windowsLineEndings () =
|
||||
expect {
|
||||
snapshot "Line 1\r\nLine 2\r\nLine 3"
|
||||
return "crlf"
|
||||
}
|
28
WoofWare.Expect.Test/SyntaxCases/RegexMetacharacters.fs
Normal file
28
WoofWare.Expect.Test/SyntaxCases/RegexMetacharacters.fs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let regexChars () =
|
||||
expect {
|
||||
snapshot @"test with regex chars: .*+?[]{}()|^$\ and more"
|
||||
return 123
|
||||
}
|
||||
|
||||
let regexInTripleQuote () =
|
||||
expect {
|
||||
snapshot """regex: .*+?[]{}()|^$\ in triple quotes"""
|
||||
return 456
|
||||
}
|
||||
|
||||
let regexInRegularString () =
|
||||
expect {
|
||||
snapshot "escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\"
|
||||
return 789
|
||||
}
|
||||
|
||||
let complexRegexPattern () =
|
||||
expect {
|
||||
snapshotJson @"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
|
||||
return "IP regex"
|
||||
}
|
47
WoofWare.Expect.Test/SyntaxCases/UnicodeCharacters.fs
Normal file
47
WoofWare.Expect.Test/SyntaxCases/UnicodeCharacters.fs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @"Hello 👋 World 🌍 with emoji 🎉🎊"
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """Chinese: 你好世界"""
|
||||
return "hello"
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @"Arabic RTL: مرحبا بالعالم"
|
||||
return "rtl test"
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot "test with combining: e\u0301 and a\u0308"
|
||||
return "combining"
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @"Mixed: English, русский, 日本語, العربية, emoji 🚀"
|
||||
return [ "multilingual" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @"Zerowidthspacetest" // Contains U+200B
|
||||
return "zwsp"
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1"""
|
||||
return "math"
|
||||
}
|
14
WoofWare.Expect.Test/TestExceptionThrowing.fs
Normal file
14
WoofWare.Expect.Test/TestExceptionThrowing.fs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace WoofWare.Expect.Test
|
||||
|
||||
open NUnit.Framework
|
||||
open WoofWare.Expect
|
||||
|
||||
[<TestFixture>]
|
||||
module TestExceptionThrowing =
|
||||
|
||||
[<Test>]
|
||||
let ``Can throw an exception`` () =
|
||||
expect {
|
||||
snapshotThrows @"System.Exception: oh no"
|
||||
return! (fun () -> failwith<int> "oh no")
|
||||
}
|
@@ -0,0 +1,313 @@
|
||||
namespace WoofWare.Expect.Test
|
||||
|
||||
open WoofWare.Expect
|
||||
open NUnit.Framework
|
||||
|
||||
[<TestFixture>]
|
||||
[<Parallelizable(ParallelScope.Children)>]
|
||||
module TestCommentsAndSpacing =
|
||||
[<OneTimeSetUp>]
|
||||
let ``Prepare to bulk-update tests`` () =
|
||||
// GlobalBuilderConfig.enterBulkUpdateMode ()
|
||||
()
|
||||
|
||||
[<OneTimeTearDown>]
|
||||
let ``Update all tests`` () =
|
||||
GlobalBuilderConfig.updateAllSnapshots ()
|
||||
|
||||
type Dummy = class end
|
||||
|
||||
[<Test>]
|
||||
let ``Multiple comments between snapshot and string`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let multipleComments () =
|
||||
expect {
|
||||
snapshot (* first comment *) (* second comment *)
|
||||
(* third comment on new line *)
|
||||
@""updated after many comments""
|
||||
|
||||
return 123
|
||||
}
|
||||
|
||||
let nestedComments () =
|
||||
expect {
|
||||
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
|
||||
return ""nested""
|
||||
}
|
||||
|
||||
let commentWithSpecialChars () =
|
||||
expect {
|
||||
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let lotsOfWhitespace () =
|
||||
expect {
|
||||
snapshot
|
||||
|
||||
|
||||
""string after whitespace""
|
||||
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let mixedWhitespaceAndComments () =
|
||||
expect {
|
||||
snapshotJson (* comment 1 *)
|
||||
(* comment 2 *)
|
||||
(* comment 3 *) @""123""
|
||||
|
||||
return 123
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 8 "updated after many comments"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Nested comments`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let multipleComments () =
|
||||
expect {
|
||||
snapshot (* first comment *) (* second comment *)
|
||||
(* third comment on new line *)
|
||||
@""test with many comments""
|
||||
|
||||
return 123
|
||||
}
|
||||
|
||||
let nestedComments () =
|
||||
expect {
|
||||
snapshot (* outer (* inner *) comment *) @""updated after nested comments""
|
||||
return ""nested""
|
||||
}
|
||||
|
||||
let commentWithSpecialChars () =
|
||||
expect {
|
||||
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let lotsOfWhitespace () =
|
||||
expect {
|
||||
snapshot
|
||||
|
||||
|
||||
""string after whitespace""
|
||||
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let mixedWhitespaceAndComments () =
|
||||
expect {
|
||||
snapshotJson (* comment 1 *)
|
||||
(* comment 2 *)
|
||||
(* comment 3 *) @""123""
|
||||
|
||||
return 123
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 17 "updated after nested comments"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Comment with special chars`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let multipleComments () =
|
||||
expect {
|
||||
snapshot (* first comment *) (* second comment *)
|
||||
(* third comment on new line *)
|
||||
@""test with many comments""
|
||||
|
||||
return 123
|
||||
}
|
||||
|
||||
let nestedComments () =
|
||||
expect {
|
||||
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
|
||||
return ""nested""
|
||||
}
|
||||
|
||||
let commentWithSpecialChars () =
|
||||
expect {
|
||||
snapshot (* comment with ""quotes"" and \ backslash *) @""updated after weird comment""
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let lotsOfWhitespace () =
|
||||
expect {
|
||||
snapshot
|
||||
|
||||
|
||||
""string after whitespace""
|
||||
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let mixedWhitespaceAndComments () =
|
||||
expect {
|
||||
snapshotJson (* comment 1 *)
|
||||
(* comment 2 *)
|
||||
(* comment 3 *) @""123""
|
||||
|
||||
return 123
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 23 "updated after weird comment"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Whitespace before`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let multipleComments () =
|
||||
expect {
|
||||
snapshot (* first comment *) (* second comment *)
|
||||
(* third comment on new line *)
|
||||
@""test with many comments""
|
||||
|
||||
return 123
|
||||
}
|
||||
|
||||
let nestedComments () =
|
||||
expect {
|
||||
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
|
||||
return ""nested""
|
||||
}
|
||||
|
||||
let commentWithSpecialChars () =
|
||||
expect {
|
||||
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let lotsOfWhitespace () =
|
||||
expect {
|
||||
snapshot
|
||||
|
||||
|
||||
@""updated after spaces""
|
||||
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let mixedWhitespaceAndComments () =
|
||||
expect {
|
||||
snapshotJson (* comment 1 *)
|
||||
(* comment 2 *)
|
||||
(* comment 3 *) @""123""
|
||||
|
||||
return 123
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 29 "updated after spaces"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Mixed whitespace and comments`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let multipleComments () =
|
||||
expect {
|
||||
snapshot (* first comment *) (* second comment *)
|
||||
(* third comment on new line *)
|
||||
@""test with many comments""
|
||||
|
||||
return 123
|
||||
}
|
||||
|
||||
let nestedComments () =
|
||||
expect {
|
||||
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
|
||||
return ""nested""
|
||||
}
|
||||
|
||||
let commentWithSpecialChars () =
|
||||
expect {
|
||||
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let lotsOfWhitespace () =
|
||||
expect {
|
||||
snapshot
|
||||
|
||||
|
||||
""string after whitespace""
|
||||
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let mixedWhitespaceAndComments () =
|
||||
expect {
|
||||
snapshotJson (* comment 1 *)
|
||||
(* comment 2 *)
|
||||
(* comment 3 *) @""updated after comments""
|
||||
|
||||
return 123
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 39 "updated after comments"
|
||||
|> String.concat "\n"
|
||||
}
|
781
WoofWare.Expect.Test/TestSnapshotFinding/TestEdgeCases.fs
Normal file
781
WoofWare.Expect.Test/TestSnapshotFinding/TestEdgeCases.fs
Normal file
@@ -0,0 +1,781 @@
|
||||
namespace WoofWare.Expect.Test
|
||||
|
||||
open NUnit.Framework
|
||||
open WoofWare.Expect
|
||||
|
||||
[<TestFixture>]
|
||||
module TestEdgeCases =
|
||||
[<OneTimeSetUp>]
|
||||
let ``Prepare to bulk-update tests`` () =
|
||||
// GlobalBuilderConfig.enterBulkUpdateMode ()
|
||||
()
|
||||
|
||||
[<OneTimeTearDown>]
|
||||
let ``Update all tests`` () =
|
||||
GlobalBuilderConfig.updateAllSnapshots ()
|
||||
|
||||
type Dummy = class end
|
||||
|
||||
[<Test>]
|
||||
let ``Empty string replacements`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot @""now has content""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 8 "now has content"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Empty string replacements, verbatim`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""now has content""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 14 "now has content"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Empty string replacements, triple quotes`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot @""now has content""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 20 "now has content"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Empty string replacements, only whitespace`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot @""now has content""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 26 "now has content"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Quotes in quotes handling`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""Updated: He said """"What's up?"""" and replied """"Nothing much.""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine
|
||||
source
|
||||
32
|
||||
"Updated: He said \"What's up?\" and replied \"Nothing much.\""
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Backslashes galore`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""prefer\these\ones""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 38 "prefer\\these\\ones"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Very long line`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""this line is short though""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 44 "this line is short though"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Leading newlines`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""
|
||||
|
||||
Just newlines!
|
||||
|
||||
|
||||
""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Ends with newlines
|
||||
|
||||
|
||||
""""""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 52 "\n\nJust newlines!\n\n\n"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Trailing newlines`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emptyString () =
|
||||
expect {
|
||||
snapshot """"
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyVerbatim () =
|
||||
expect {
|
||||
snapshot @""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let emptyTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""""""""
|
||||
return """"
|
||||
}
|
||||
|
||||
let onlyWhitespace () =
|
||||
expect {
|
||||
snapshot "" \t\n ""
|
||||
return ""whitespace""
|
||||
}
|
||||
|
||||
let quotesInQuotes () =
|
||||
expect {
|
||||
snapshot @""He said """"Hello"""" and she said """"Hi""""""
|
||||
return ""quotes""
|
||||
}
|
||||
|
||||
let backslashesGalore () =
|
||||
expect {
|
||||
snapshot @""C:\Users\Test\Documents\file.txt""
|
||||
return ""path""
|
||||
}
|
||||
|
||||
let veryLongLine () =
|
||||
expect {
|
||||
snapshot
|
||||
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
|
||||
|
||||
return ""long line""
|
||||
}
|
||||
|
||||
let leadingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""
|
||||
|
||||
Starts with newlines""""""
|
||||
|
||||
return ""leading""
|
||||
}
|
||||
|
||||
let trailingNewlines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""
|
||||
|
||||
Just newlines!
|
||||
|
||||
|
||||
""
|
||||
|
||||
return ""trailing""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 62 "\n\nJust newlines!\n\n\n"
|
||||
|> String.concat "\n"
|
||||
}
|
483
WoofWare.Expect.Test/TestSnapshotFinding/TestMultilineComplex.fs
Normal file
483
WoofWare.Expect.Test/TestSnapshotFinding/TestMultilineComplex.fs
Normal file
@@ -0,0 +1,483 @@
|
||||
namespace WoofWare.Expect.Test
|
||||
|
||||
open NUnit.Framework
|
||||
open WoofWare.Expect
|
||||
|
||||
[<TestFixture>]
|
||||
module TestMultilineComplex =
|
||||
[<OneTimeSetUp>]
|
||||
let ``Prepare to bulk-update tests`` () =
|
||||
// GlobalBuilderConfig.enterBulkUpdateMode ()
|
||||
()
|
||||
|
||||
[<OneTimeTearDown>]
|
||||
let ``Update all tests`` () =
|
||||
GlobalBuilderConfig.updateAllSnapshots ()
|
||||
|
||||
type Dummy = class end
|
||||
|
||||
[<Test>]
|
||||
let ``Very long multiline string`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let veryLongMultiline () =
|
||||
expect {
|
||||
snapshot
|
||||
@""Replaced with
|
||||
a different
|
||||
multiline
|
||||
value""
|
||||
|
||||
return ""long""
|
||||
}
|
||||
|
||||
let multilineWithEmptyLines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""First line
|
||||
|
||||
Third line
|
||||
|
||||
|
||||
Sixth line""
|
||||
|
||||
return ""empty lines""
|
||||
}
|
||||
|
||||
let multilineWithSpecialChars () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Special chars:
|
||||
Tab: here
|
||||
Quotes: ""double"" and 'single'
|
||||
Backslash: \ and \\
|
||||
Unicode: 🎯
|
||||
Regex: .*+?[]""""""
|
||||
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let multilineJson () =
|
||||
expect {
|
||||
snapshotJson
|
||||
@""{
|
||||
""""name"""": """"test"""",
|
||||
""""values"""": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
""""nested"""": {
|
||||
""""deep"""": true
|
||||
}
|
||||
}""
|
||||
|
||||
return
|
||||
{
|
||||
name = ""test""
|
||||
values = [ 1 ; 2 ; 3 ]
|
||||
nested =
|
||||
{|
|
||||
deep = true
|
||||
|}
|
||||
}
|
||||
}
|
||||
|
||||
let windowsLineEndings () =
|
||||
expect {
|
||||
snapshot ""Line 1\r\nLine 2\r\nLine 3""
|
||||
return ""crlf""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 8 "Replaced with\na different\nmultiline\nvalue"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Multiline with empty lines`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let veryLongMultiline () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
Line 4
|
||||
Line 5
|
||||
Line 6
|
||||
Line 7
|
||||
Line 8
|
||||
Line 9
|
||||
Line 10
|
||||
Indented line 11
|
||||
More indented line 12
|
||||
Line 13
|
||||
Line 14
|
||||
Line 15""""""
|
||||
|
||||
return ""long""
|
||||
}
|
||||
|
||||
let multilineWithEmptyLines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""Replaced with
|
||||
|
||||
a different
|
||||
multiline
|
||||
value""
|
||||
|
||||
return ""empty lines""
|
||||
}
|
||||
|
||||
let multilineWithSpecialChars () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Special chars:
|
||||
Tab: here
|
||||
Quotes: ""double"" and 'single'
|
||||
Backslash: \ and \\
|
||||
Unicode: 🎯
|
||||
Regex: .*+?[]""""""
|
||||
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let multilineJson () =
|
||||
expect {
|
||||
snapshotJson
|
||||
@""{
|
||||
""""name"""": """"test"""",
|
||||
""""values"""": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
""""nested"""": {
|
||||
""""deep"""": true
|
||||
}
|
||||
}""
|
||||
|
||||
return
|
||||
{
|
||||
name = ""test""
|
||||
values = [ 1 ; 2 ; 3 ]
|
||||
nested =
|
||||
{|
|
||||
deep = true
|
||||
|}
|
||||
}
|
||||
}
|
||||
|
||||
let windowsLineEndings () =
|
||||
expect {
|
||||
snapshot ""Line 1\r\nLine 2\r\nLine 3""
|
||||
return ""crlf""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 30 "Replaced with\n\na different\nmultiline\nvalue"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Multiline with special chars`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let veryLongMultiline () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
Line 4
|
||||
Line 5
|
||||
Line 6
|
||||
Line 7
|
||||
Line 8
|
||||
Line 9
|
||||
Line 10
|
||||
Indented line 11
|
||||
More indented line 12
|
||||
Line 13
|
||||
Line 14
|
||||
Line 15""""""
|
||||
|
||||
return ""long""
|
||||
}
|
||||
|
||||
let multilineWithEmptyLines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""First line
|
||||
|
||||
Third line
|
||||
|
||||
|
||||
Sixth line""
|
||||
|
||||
return ""empty lines""
|
||||
}
|
||||
|
||||
let multilineWithSpecialChars () =
|
||||
expect {
|
||||
snapshot
|
||||
@""get rid of it all""
|
||||
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let multilineJson () =
|
||||
expect {
|
||||
snapshotJson
|
||||
@""{
|
||||
""""name"""": """"test"""",
|
||||
""""values"""": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
""""nested"""": {
|
||||
""""deep"""": true
|
||||
}
|
||||
}""
|
||||
|
||||
return
|
||||
{
|
||||
name = ""test""
|
||||
values = [ 1 ; 2 ; 3 ]
|
||||
nested =
|
||||
{|
|
||||
deep = true
|
||||
|}
|
||||
}
|
||||
}
|
||||
|
||||
let windowsLineEndings () =
|
||||
expect {
|
||||
snapshot ""Line 1\r\nLine 2\r\nLine 3""
|
||||
return ""crlf""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 43 "get rid of it all"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Complex nested JSON with Unicode`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let veryLongMultiline () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
Line 4
|
||||
Line 5
|
||||
Line 6
|
||||
Line 7
|
||||
Line 8
|
||||
Line 9
|
||||
Line 10
|
||||
Indented line 11
|
||||
More indented line 12
|
||||
Line 13
|
||||
Line 14
|
||||
Line 15""""""
|
||||
|
||||
return ""long""
|
||||
}
|
||||
|
||||
let multilineWithEmptyLines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""First line
|
||||
|
||||
Third line
|
||||
|
||||
|
||||
Sixth line""
|
||||
|
||||
return ""empty lines""
|
||||
}
|
||||
|
||||
let multilineWithSpecialChars () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Special chars:
|
||||
Tab: here
|
||||
Quotes: ""double"" and 'single'
|
||||
Backslash: \ and \\
|
||||
Unicode: 🎯
|
||||
Regex: .*+?[]""""""
|
||||
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let multilineJson () =
|
||||
expect {
|
||||
snapshotJson
|
||||
@""wheeeee""
|
||||
|
||||
return
|
||||
{
|
||||
name = ""test""
|
||||
values = [ 1 ; 2 ; 3 ]
|
||||
nested =
|
||||
{|
|
||||
deep = true
|
||||
|}
|
||||
}
|
||||
}
|
||||
|
||||
let windowsLineEndings () =
|
||||
expect {
|
||||
snapshot ""Line 1\r\nLine 2\r\nLine 3""
|
||||
return ""crlf""
|
||||
}
|
||||
"
|
||||
|
||||
return SnapshotUpdate.updateSnapshotAtLine source 56 "wheeeee" |> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Windows line endings`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let veryLongMultiline () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
Line 4
|
||||
Line 5
|
||||
Line 6
|
||||
Line 7
|
||||
Line 8
|
||||
Line 9
|
||||
Line 10
|
||||
Indented line 11
|
||||
More indented line 12
|
||||
Line 13
|
||||
Line 14
|
||||
Line 15""""""
|
||||
|
||||
return ""long""
|
||||
}
|
||||
|
||||
let multilineWithEmptyLines () =
|
||||
expect {
|
||||
snapshot
|
||||
@""First line
|
||||
|
||||
Third line
|
||||
|
||||
|
||||
Sixth line""
|
||||
|
||||
return ""empty lines""
|
||||
}
|
||||
|
||||
let multilineWithSpecialChars () =
|
||||
expect {
|
||||
snapshot
|
||||
""""""Special chars:
|
||||
Tab: here
|
||||
Quotes: ""double"" and 'single'
|
||||
Backslash: \ and \\
|
||||
Unicode: 🎯
|
||||
Regex: .*+?[]""""""
|
||||
|
||||
return ""special""
|
||||
}
|
||||
|
||||
let multilineJson () =
|
||||
expect {
|
||||
snapshotJson
|
||||
@""{
|
||||
""""name"""": """"test"""",
|
||||
""""values"""": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
""""nested"""": {
|
||||
""""deep"""": true
|
||||
}
|
||||
}""
|
||||
|
||||
return
|
||||
{
|
||||
name = ""test""
|
||||
values = [ 1 ; 2 ; 3 ]
|
||||
nested =
|
||||
{|
|
||||
deep = true
|
||||
|}
|
||||
}
|
||||
}
|
||||
|
||||
let windowsLineEndings () =
|
||||
expect {
|
||||
snapshot @""down with line endings""
|
||||
return ""crlf""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 82 "down with line endings"
|
||||
|> String.concat "\n"
|
||||
}
|
@@ -0,0 +1,190 @@
|
||||
namespace WoofWare.Expect.Test
|
||||
|
||||
open WoofWare.Expect
|
||||
open NUnit.Framework
|
||||
|
||||
[<TestFixture>]
|
||||
[<Parallelizable(ParallelScope.Children)>]
|
||||
module TestRegexMetacharacters =
|
||||
[<OneTimeSetUp>]
|
||||
let ``Prepare to bulk-update tests`` () =
|
||||
// GlobalBuilderConfig.enterBulkUpdateMode ()
|
||||
()
|
||||
|
||||
[<OneTimeTearDown>]
|
||||
let ``Update all tests`` () =
|
||||
GlobalBuilderConfig.updateAllSnapshots ()
|
||||
|
||||
type Dummy = class end
|
||||
|
||||
[<Test>]
|
||||
let ``Regex metacharacters in verbatim string`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let regexChars () =
|
||||
expect {
|
||||
snapshot @""replacement with .*+?[]{}()|^$\ chars""
|
||||
return 123
|
||||
}
|
||||
|
||||
let regexInTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""regex: .*+?[]{}()|^$\ in triple quotes""""""
|
||||
return 456
|
||||
}
|
||||
|
||||
let regexInRegularString () =
|
||||
expect {
|
||||
snapshot ""escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\""
|
||||
return 789
|
||||
}
|
||||
|
||||
let complexRegexPattern () =
|
||||
expect {
|
||||
snapshotJson @""^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$""
|
||||
return ""IP regex""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 8 "replacement with .*+?[]{}()|^$\\ chars"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Regex metacharacters in triple quote`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let regexChars () =
|
||||
expect {
|
||||
snapshot @""test with regex chars: .*+?[]{}()|^$\ and more""
|
||||
return 123
|
||||
}
|
||||
|
||||
let regexInTripleQuote () =
|
||||
expect {
|
||||
snapshot @""new regex: [a-z]+(?:\d{2,4})? pattern""
|
||||
return 456
|
||||
}
|
||||
|
||||
let regexInRegularString () =
|
||||
expect {
|
||||
snapshot ""escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\""
|
||||
return 789
|
||||
}
|
||||
|
||||
let complexRegexPattern () =
|
||||
expect {
|
||||
snapshotJson @""^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$""
|
||||
return ""IP regex""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 14 "new regex: [a-z]+(?:\\d{2,4})? pattern"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Regex metacharacters in regular string`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let regexChars () =
|
||||
expect {
|
||||
snapshot @""test with regex chars: .*+?[]{}()|^$\ and more""
|
||||
return 123
|
||||
}
|
||||
|
||||
let regexInTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""regex: .*+?[]{}()|^$\ in triple quotes""""""
|
||||
return 456
|
||||
}
|
||||
|
||||
let regexInRegularString () =
|
||||
expect {
|
||||
snapshot @""new regex: [a-z]+(?:\d{2,4})? pattern""
|
||||
return 789
|
||||
}
|
||||
|
||||
let complexRegexPattern () =
|
||||
expect {
|
||||
snapshotJson @""^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$""
|
||||
return ""IP regex""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 20 "new regex: [a-z]+(?:\\d{2,4})? pattern"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``IP regex`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let regexChars () =
|
||||
expect {
|
||||
snapshot @""test with regex chars: .*+?[]{}()|^$\ and more""
|
||||
return 123
|
||||
}
|
||||
|
||||
let regexInTripleQuote () =
|
||||
expect {
|
||||
snapshot """"""regex: .*+?[]{}()|^$\ in triple quotes""""""
|
||||
return 456
|
||||
}
|
||||
|
||||
let regexInRegularString () =
|
||||
expect {
|
||||
snapshot ""escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\""
|
||||
return 789
|
||||
}
|
||||
|
||||
let complexRegexPattern () =
|
||||
expect {
|
||||
snapshotJson @""new regex: [a-z]+(?:\d{2,4})? pattern""
|
||||
return ""IP regex""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 26 "new regex: [a-z]+(?:\\d{2,4})? pattern"
|
||||
|> String.concat "\n"
|
||||
}
|
@@ -4,7 +4,16 @@ open WoofWare.Expect
|
||||
open NUnit.Framework
|
||||
|
||||
[<TestFixture>]
|
||||
[<Parallelizable(ParallelScope.Children)>]
|
||||
module TestSnapshotFinding =
|
||||
[<OneTimeSetUp>]
|
||||
let ``Prepare to bulk-update tests`` () =
|
||||
// GlobalBuilderConfig.enterBulkUpdateMode ()
|
||||
()
|
||||
|
||||
[<OneTimeTearDown>]
|
||||
let ``Update all tests`` () =
|
||||
GlobalBuilderConfig.updateAllSnapshots ()
|
||||
|
||||
type Dummy = class end
|
||||
|
@@ -0,0 +1,456 @@
|
||||
namespace WoofWare.Expect.Test
|
||||
|
||||
open NUnit.Framework
|
||||
open WoofWare.Expect
|
||||
|
||||
[<TestFixture>]
|
||||
module TestUnicodeCharacters =
|
||||
[<OneTimeSetUp>]
|
||||
let ``Prepare to bulk-update tests`` () =
|
||||
// GlobalBuilderConfig.enterBulkUpdateMode ()
|
||||
()
|
||||
|
||||
[<OneTimeTearDown>]
|
||||
let ``Update all tests`` () =
|
||||
GlobalBuilderConfig.updateAllSnapshots ()
|
||||
|
||||
type Dummy = class end
|
||||
|
||||
[<Test>]
|
||||
let ``Unicode emoji in string`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Updated with 🚀🌟✨ more emoji!""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """"""Chinese: 你好世界""""""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Arabic RTL: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot ""test with combining: e\u0301 and a\u0308""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """"""Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""""""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 8 "Updated with 🚀🌟✨ more emoji!"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Unicode Chinese characters multi-line`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot @""Chinese poem:
|
||||
静夜思
|
||||
床前明月光
|
||||
疑是地上霜
|
||||
举头望明月
|
||||
低头思故乡""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Arabic RTL: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot ""test with combining: e\u0301 and a\u0308""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """"""Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""""""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 14 "Chinese poem:\n静夜思\n床前明月光\n疑是地上霜\n举头望明月\n低头思故乡"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Arabic RTL`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """"""Chinese: 你好世界""""""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Updated Arabic: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot ""test with combining: e\u0301 and a\u0308""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """"""Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""""""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 20 "Updated Arabic: مرحبا بالعالم"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Combining characters`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """"""Chinese: 你好世界""""""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Arabic RTL: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot @""updated test with combining: é and ä!""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """"""Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""""""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 27 "updated test with combining: e\u0301 and a\u0308!"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Mixed scripts`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """"""Chinese: 你好世界""""""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Arabic RTL: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot ""test with combining: e\u0301 and a\u0308""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Updated mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """"""Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""""""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 33 "Updated mixed: English, русский, 日本語, العربية, emoji 🚀"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``ZWBS character`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """"""Chinese: 你好世界""""""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Arabic RTL: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot ""test with combining: e\u0301 and a\u0308""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Updated: Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot """"""Math: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""""""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 39 "Updated: Zerowidthspacetest"
|
||||
|> String.concat "\n"
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``Maths`` () =
|
||||
let source =
|
||||
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|
||||
|> _.Split('\n')
|
||||
|
||||
expect {
|
||||
snapshot
|
||||
@"namespace BigExample
|
||||
|
||||
open WoofWare.Expect
|
||||
|
||||
module MyModule =
|
||||
let emoji () =
|
||||
expect {
|
||||
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
|
||||
return 123
|
||||
}
|
||||
|
||||
let chineseCharacters () =
|
||||
expect {
|
||||
snapshot """"""Chinese: 你好世界""""""
|
||||
return ""hello""
|
||||
}
|
||||
|
||||
let arabicRTL () =
|
||||
expect {
|
||||
snapshot @""Arabic RTL: مرحبا بالعالم""
|
||||
return ""rtl test""
|
||||
}
|
||||
|
||||
let combiningCharacters () =
|
||||
expect {
|
||||
// Combining diacritics: e + ́ = é
|
||||
snapshot ""test with combining: e\u0301 and a\u0308""
|
||||
return ""combining""
|
||||
}
|
||||
|
||||
let mixedScripts () =
|
||||
expect {
|
||||
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
|
||||
return [ ""multilingual"" ]
|
||||
}
|
||||
|
||||
let zeroWidthChars () =
|
||||
expect {
|
||||
snapshot @""Zerowidthspacetest"" // Contains U+200B
|
||||
return ""zwsp""
|
||||
}
|
||||
|
||||
let mathSymbols () =
|
||||
expect {
|
||||
snapshot @""Pretty vacuous, huh: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1""
|
||||
return ""math""
|
||||
}
|
||||
"
|
||||
|
||||
return
|
||||
SnapshotUpdate.updateSnapshotAtLine source 45 "Pretty vacuous, huh: ∀x∈ℝ, ∃y: x² + y² = 1 ⟹ |x| ≤ 1"
|
||||
|> String.concat "\n"
|
||||
}
|
@@ -1,21 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EnableNUnitRunner>true</EnableNUnitRunner>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Assembly.fs" />
|
||||
<Compile Include="BulkUpdateExample.fs" />
|
||||
<Compile Include="SimpleTest.fs" />
|
||||
<Compile Include="TestSnapshotFinding.fs" />
|
||||
<Compile Include="TestExceptionThrowing.fs" />
|
||||
<Compile Include="TestSurface.fs" />
|
||||
<Compile Include="TestSnapshotFinding\TestSnapshotFinding.fs" />
|
||||
<Compile Include="TestSnapshotFinding\TestUnicodeCharacters.fs" />
|
||||
<Compile Include="TestSnapshotFinding\TestMultilineComplex.fs" />
|
||||
<Compile Include="TestSnapshotFinding\TestEdgeCases.fs" />
|
||||
<Compile Include="TestSnapshotFinding\TestCommentsAndSpacing.fs" />
|
||||
<Compile Include="TestSnapshotFinding\TestRegexMetacharacters.fs" />
|
||||
|
||||
<EmbeddedResource Include="SyntaxCases\AtStringOneLine.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\SingleQuoteManyLine.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\TripleQuoteInterveningComment.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\TripleQuoteOneLine.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\RegexMetacharacters.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\UnicodeCharacters.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\MultilineComplex.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\EdgeCases.fs" />
|
||||
<EmbeddedResource Include="SyntaxCases\CommentsAndSpacing.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -23,8 +23,6 @@ type Mode =
|
||||
/// <param name="applyChanges">When running the tests, instead of throwing an exception on failure, update the snapshot.</param>
|
||||
/// <param name="sourceOverride">Override the file path and line numbers reported in snapshots, so that your tests can be fully stable even on failure. (You almost certainly don't want to set this.)</param>
|
||||
type ExpectBuilder (mode : Mode) =
|
||||
member private this.Mode = Unchecked.defaultof<Mode>
|
||||
|
||||
new (sourceOverride : string * int) = ExpectBuilder (Mode.AssertMockingSource sourceOverride)
|
||||
|
||||
new (update : bool)
|
||||
@@ -144,6 +142,51 @@ type ExpectBuilder (mode : Mode) =
|
||||
Actual = None
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expresses that the given expression throws during evaluation.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// expect {
|
||||
/// snapshotThrows @"System.Exception: oh no"
|
||||
/// return! (fun () -> failwith "oh no")
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[<CustomOperation("snapshotThrows", MaintainsVariableSpaceUsingBind = true)>]
|
||||
member _.SnapshotThrows<'a>
|
||||
(
|
||||
state : ExpectState<'a>,
|
||||
snapshot : string,
|
||||
[<CallerMemberName>] ?memberName : string,
|
||||
[<CallerLineNumber>] ?callerLine : int,
|
||||
[<CallerFilePath>] ?filePath : string
|
||||
)
|
||||
: ExpectState<'a>
|
||||
=
|
||||
match state.Snapshot with
|
||||
| Some _ -> failwith "snapshot can only be specified once"
|
||||
| None ->
|
||||
|
||||
let memberName = defaultArg memberName "<unknown method>"
|
||||
let filePath = defaultArg filePath "<unknown file>"
|
||||
let lineNumber = defaultArg callerLine -1
|
||||
|
||||
let callerInfo =
|
||||
{
|
||||
MemberName = memberName
|
||||
FilePath = filePath
|
||||
LineNumber = lineNumber
|
||||
}
|
||||
|
||||
{
|
||||
Formatter = None
|
||||
JsonSerialiserOptions = state.JsonSerialiserOptions
|
||||
JsonDocOptions = state.JsonDocOptions
|
||||
Snapshot = Some (SnapshotValue.ThrowsException snapshot, callerInfo)
|
||||
Actual = None
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Express that the <c>return</c> value of this builder should be formatted using this function, before
|
||||
/// comparing to the snapshot.
|
||||
@@ -158,7 +201,7 @@ type ExpectBuilder (mode : Mode) =
|
||||
| Some _ -> failwith "Please don't supply withFormat more than once"
|
||||
| None ->
|
||||
{ state with
|
||||
Formatter = Some formatter
|
||||
Formatter = Some (fun f -> f () |> formatter)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -226,6 +269,17 @@ type ExpectBuilder (mode : Mode) =
|
||||
|
||||
/// Expresses the "actual value" component of the assertion "expected snapshot = actual value".
|
||||
member _.Return (value : 'T) : ExpectState<'T> =
|
||||
{
|
||||
Snapshot = None
|
||||
Formatter = None
|
||||
JsonDocOptions = None
|
||||
JsonSerialiserOptions = None
|
||||
Actual = Some (fun () -> value)
|
||||
}
|
||||
|
||||
/// Expresses the "actual value" component of the assertion "expected snapshot = actual value", but delayed behind
|
||||
/// a function (by contrast with `Return`).
|
||||
member _.ReturnFrom (value : unit -> 'T) : ExpectState<'T> =
|
||||
{
|
||||
Snapshot = None
|
||||
Formatter = None
|
||||
@@ -254,7 +308,7 @@ type ExpectBuilder (mode : Mode) =
|
||||
|> ExpectException
|
||||
|> raise
|
||||
| Mode.Assert ->
|
||||
if GlobalBuilderConfig.bulkUpdate.Value > 0 then
|
||||
if GlobalBuilderConfig.isBulkUpdateMode () then
|
||||
GlobalBuilderConfig.registerTest state
|
||||
else
|
||||
sprintf
|
||||
@@ -270,14 +324,14 @@ type ExpectBuilder (mode : Mode) =
|
||||
let lines = File.ReadAllLines state.Caller.FilePath
|
||||
let oldContents = String.concat "\n" lines
|
||||
let lines = SnapshotUpdate.updateSnapshotAtLine lines state.Caller.LineNumber actual
|
||||
File.WriteAllLines (state.Caller.FilePath, lines)
|
||||
File.writeAllLines lines state.Caller.FilePath
|
||||
failwith ("Snapshot successfully updated. Previous contents:\n" + oldContents)
|
||||
|
||||
match CompletedSnapshotGeneric.passesAssertion state with
|
||||
| None ->
|
||||
match mode, GlobalBuilderConfig.bulkUpdate.Value with
|
||||
match mode, GlobalBuilderConfig.isBulkUpdateMode () with
|
||||
| Mode.Update, _
|
||||
| _, 1 ->
|
||||
| _, true ->
|
||||
failwith
|
||||
"Snapshot assertion passed, but we are in snapshot-updating mode. Use the `expect` builder instead of `expect'` to assert the contents of a single snapshot; disable `GlobalBuilderConfig.bulkUpdate` to move back to assertion-checking mode."
|
||||
| _ -> ()
|
||||
|
@@ -1,12 +1,19 @@
|
||||
namespace WoofWare.Expect
|
||||
|
||||
open System.Threading
|
||||
|
||||
/// Module holding global mutable state controlling the behaviour of WoofWare.Expect
|
||||
/// when running in bulk-update mode.
|
||||
[<RequireQualifiedAccess>]
|
||||
module GlobalBuilderConfig =
|
||||
let internal bulkUpdate = ref 0
|
||||
/// All access to the global mutable state locks on this.
|
||||
let private locker = obj ()
|
||||
|
||||
// Global mutable state ensuring there is at most one `enterBulkUpdateMode`/`updateAllSnapshots` pair running at once.
|
||||
let private bulkUpdate = ref 0
|
||||
|
||||
let private allTests : ResizeArray<CompletedSnapshot> = ResizeArray ()
|
||||
|
||||
let internal isBulkUpdateMode () : bool =
|
||||
lock locker (fun () -> bulkUpdate.Value > 0)
|
||||
|
||||
/// <summary>
|
||||
/// Call this to make the <c>expect</c> builder register all tests for bulk update as it runs.
|
||||
@@ -16,11 +23,15 @@ module GlobalBuilderConfig =
|
||||
/// The implied global mutable state is liable to interfere with other expect builders in other fixtures otherwise.
|
||||
/// </remarks>
|
||||
let enterBulkUpdateMode () =
|
||||
if Interlocked.Increment bulkUpdate <> 1 then
|
||||
failwith
|
||||
"WoofWare.Expect requires bulk updates to happen serially: for example, make the test fixture `[<NonParallelizable>]` if you're using NUnit."
|
||||
lock
|
||||
locker
|
||||
(fun () ->
|
||||
if bulkUpdate.Value <> 0 then
|
||||
failwith
|
||||
"WoofWare.Expect requires bulk updates to happen serially: for example, make the test fixture `[<NonParallelizable>]` if you're using NUnit."
|
||||
|
||||
let private allTests : ResizeArray<CompletedSnapshot> = ResizeArray ()
|
||||
bulkUpdate.Value <- bulkUpdate.Value + 1
|
||||
)
|
||||
|
||||
/// <summary>
|
||||
/// Clear the set of failing tests registered by any previous bulk-update runs.
|
||||
@@ -30,23 +41,31 @@ module GlobalBuilderConfig =
|
||||
/// You probably don't need to do this, because your test runner is probably tearing down
|
||||
/// anyway after the tests have failed; this is mainly here for WoofWare.Expect's own internal testing.
|
||||
/// </remarks>
|
||||
let clearTests () = lock allTests allTests.Clear
|
||||
let clearTests () = lock locker allTests.Clear
|
||||
|
||||
let internal registerTest (s : CompletedSnapshotGeneric<'T>) : unit =
|
||||
let toAdd = s |> CompletedSnapshot.make
|
||||
lock allTests (fun () -> allTests.Add toAdd)
|
||||
lock locker (fun () -> allTests.Add toAdd)
|
||||
|
||||
/// <summary>
|
||||
/// For all tests whose failures have already been registered,
|
||||
/// transform the files on disk so that the failing snapshots now pass.
|
||||
/// </summary>
|
||||
let updateAllSnapshots () =
|
||||
let bulkUpdate' = Interlocked.Decrement bulkUpdate
|
||||
// It's OK for this to be called when `enterBulkUpdateMode` has not been called, i.e. when `bulkUpdate` has
|
||||
// value 0. That just means we aren't in bulk-update mode, so we expect the following simply to do nothing.
|
||||
// (This is an expected workflow: we expect users to run `updateAllSnapshots` unconditionally in a
|
||||
// one-time tear-down of the test suite, and they use the one-time setup to control whether any work is actually
|
||||
// performed here.)
|
||||
lock
|
||||
locker
|
||||
(fun () ->
|
||||
let allTests = Seq.toArray allTests
|
||||
|
||||
try
|
||||
if bulkUpdate' = 0 then
|
||||
let allTests = lock allTests (fun () -> Seq.toArray allTests)
|
||||
SnapshotUpdate.updateAll allTests
|
||||
|
||||
finally
|
||||
clearTests ()
|
||||
try
|
||||
SnapshotUpdate.updateAll allTests
|
||||
finally
|
||||
// double acquiring of reentrant lock is OK, we're not switching threads
|
||||
clearTests ()
|
||||
bulkUpdate.Value <- 0
|
||||
)
|
||||
|
@@ -17,20 +17,21 @@ type CallerInfo =
|
||||
type private SnapshotValue =
|
||||
| Json of expected : string
|
||||
| Formatted of expected : string
|
||||
| ThrowsException of expected : string
|
||||
|
||||
type private CompletedSnapshotValue<'T> =
|
||||
| Json of expected : string * JsonSerializerOptions * JsonDocumentOptions
|
||||
| Formatted of expected : string * format : ('T -> string)
|
||||
| Formatted of expected : string * format : ((unit -> 'T) -> string)
|
||||
|
||||
/// The state accumulated by the `expect` builder. You should never find yourself interacting with this type.
|
||||
type ExpectState<'T> =
|
||||
private
|
||||
{
|
||||
Formatter : ('T -> string) option
|
||||
Formatter : ((unit -> 'T) -> string) option
|
||||
JsonSerialiserOptions : JsonSerializerOptions option
|
||||
JsonDocOptions : JsonDocumentOptions option
|
||||
Snapshot : (SnapshotValue * CallerInfo) option
|
||||
Actual : 'T option
|
||||
Actual : (unit -> 'T) option
|
||||
}
|
||||
|
||||
/// The state accumulated by the `expect` builder. You should never find yourself interacting with this type.
|
||||
@@ -39,7 +40,7 @@ type internal CompletedSnapshotGeneric<'T> =
|
||||
{
|
||||
SnapshotValue : CompletedSnapshotValue<'T>
|
||||
Caller : CallerInfo
|
||||
Actual : 'T
|
||||
Actual : unit -> 'T
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
@@ -68,11 +69,22 @@ module internal CompletedSnapshotGeneric =
|
||||
| SnapshotValue.Formatted expected ->
|
||||
let formatter =
|
||||
match state.Formatter with
|
||||
| None -> fun x -> x.ToString ()
|
||||
| None -> fun x -> x().ToString ()
|
||||
| Some f -> f
|
||||
|
||||
CompletedSnapshotValue.Formatted (expected, formatter)
|
||||
|
||||
| SnapshotValue.ThrowsException expected ->
|
||||
CompletedSnapshotValue.Formatted (
|
||||
expected,
|
||||
fun x ->
|
||||
try
|
||||
x () |> ignore
|
||||
"<no exception raised>"
|
||||
with e ->
|
||||
e.GetType().FullName + ": " + e.Message
|
||||
)
|
||||
|
||||
{
|
||||
SnapshotValue = snapshot
|
||||
Caller = source
|
||||
@@ -84,7 +96,7 @@ module internal CompletedSnapshotGeneric =
|
||||
let internal replacement (s : CompletedSnapshotGeneric<'T>) =
|
||||
match s.SnapshotValue with
|
||||
| CompletedSnapshotValue.Json (_existing, options, _) ->
|
||||
JsonSerializer.Serialize (s.Actual, options)
|
||||
JsonSerializer.Serialize (s.Actual (), options)
|
||||
|> JsonDocument.Parse
|
||||
|> _.RootElement
|
||||
|> _.ToString()
|
||||
@@ -104,7 +116,7 @@ module internal CompletedSnapshotGeneric =
|
||||
None
|
||||
|
||||
let canonicalActual =
|
||||
JsonSerializer.Serialize (state.Actual, jsonSerOptions) |> JsonDocument.Parse
|
||||
JsonSerializer.Serialize (state.Actual (), jsonSerOptions) |> JsonDocument.Parse
|
||||
|
||||
match canonicalSnapshot with
|
||||
| None -> Some ("[JSON failed to parse:] " + snapshot, canonicalActual.RootElement.ToString ())
|
||||
|
32
WoofWare.Expect/File.fs
Normal file
32
WoofWare.Expect/File.fs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace WoofWare.Expect
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module internal File =
|
||||
|
||||
/// Standard attempt at an atomic file write.
|
||||
/// It may fail to be atomic if the working directory somehow spans multiple volumes,
|
||||
/// and of course with external network storage all bets are off.
|
||||
let writeAllLines (lines : string[]) (path : string) : unit =
|
||||
let file = FileInfo path
|
||||
|
||||
let tempFile =
|
||||
Path.Combine (file.Directory.FullName, file.Name + "." + Guid.NewGuid().ToString () + ".tmp")
|
||||
|
||||
try
|
||||
File.WriteAllLines (tempFile, lines)
|
||||
// Atomicity guarantees are undocumented, but on Unix this is an atomic `rename` call
|
||||
// https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs#L181
|
||||
// and on Windows this is an atomic ReplaceFile:
|
||||
// https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs#L92
|
||||
// calls https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs#L12
|
||||
// which calls ReplaceFileW, whose atomicity guarantees are again apparently undocumented,
|
||||
// but 4o-turbo, Opus 4, and Gemini 2.5 Flash all think it's atomic.
|
||||
File.Replace (tempFile, path, null)
|
||||
finally
|
||||
try
|
||||
File.Delete tempFile
|
||||
with _ ->
|
||||
()
|
@@ -141,13 +141,14 @@ module internal SnapshotUpdate =
|
||||
else
|
||||
// We need to include enough lines to capture multi-line strings
|
||||
// Take a reasonable number of lines after the snapshot line
|
||||
let maxLines = min 50 (lines.Length - startIdx)
|
||||
let maxLines = lines.Length - startIdx
|
||||
let relevantLines = lines |> Array.skip startIdx |> Array.take maxLines
|
||||
|
||||
let searchText = String.concat "\n" relevantLines
|
||||
|
||||
// Find snapshot keyword
|
||||
let snapshotMatch = Regex.Match (searchText, @"\b(snapshot|snapshotJson)\b")
|
||||
let snapshotMatch =
|
||||
Regex.Match (searchText, @"\b(snapshot|snapshotJson|snapshotThrows)\b")
|
||||
|
||||
if not snapshotMatch.Success then
|
||||
None
|
||||
@@ -300,5 +301,5 @@ module internal SnapshotUpdate =
|
||||
|
||||
let newContents = updateAllLines contents sources
|
||||
|
||||
System.IO.File.WriteAllLines (callerFile, newContents)
|
||||
File.writeAllLines newContents callerFile
|
||||
)
|
||||
|
@@ -16,9 +16,11 @@ WoofWare.Expect.ExpectBuilder.Bind [method]: ('U WoofWare.Expect.ExpectState, un
|
||||
WoofWare.Expect.ExpectBuilder.Delay [method]: (unit -> 'T WoofWare.Expect.ExpectState) -> (unit -> 'T WoofWare.Expect.ExpectState)
|
||||
WoofWare.Expect.ExpectBuilder.Return [method]: 'T -> 'T WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.Return [method]: unit -> 'T WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.ReturnFrom [method]: (unit -> 'T) -> 'T WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.Run [method]: (unit -> 'T WoofWare.Expect.ExpectState) -> unit
|
||||
WoofWare.Expect.ExpectBuilder.Snapshot [method]: ('a WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.SnapshotJson [method]: (unit WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.SnapshotThrows [method]: ('a WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.WithFormat [method]: ('T WoofWare.Expect.ExpectState, 'T -> string) -> 'T WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.WithJsonDocOptions [method]: ('T WoofWare.Expect.ExpectState, System.Text.Json.JsonDocumentOptions) -> 'T WoofWare.Expect.ExpectState
|
||||
WoofWare.Expect.ExpectBuilder.WithJsonSerializerOptions [method]: ('T WoofWare.Expect.ExpectState, System.Text.Json.JsonSerializerOptions) -> 'T WoofWare.Expect.ExpectState
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyInfo.fs" />
|
||||
<Compile Include="Text.fs" />
|
||||
<Compile Include="File.fs" />
|
||||
<Compile Include="Domain.fs" />
|
||||
<Compile Include="SnapshotUpdate.fs" />
|
||||
<Compile Include="Config.fs" />
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.4",
|
||||
"version": "0.5",
|
||||
"publicReleaseRefSpec": [
|
||||
"^refs/heads/main$"
|
||||
],
|
||||
@@ -10,4 +10,4 @@
|
||||
":/Directory.Build.props",
|
||||
":/LICENSE"
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user