Expect.Elmish and restructure sample/test build
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,9 +1,6 @@
|
||||
*.fs.js
|
||||
*/public/*.js
|
||||
*/public/*.txt
|
||||
*/public/*.png
|
||||
out/
|
||||
bundle.js*
|
||||
build/sample/*
|
||||
build/test/*
|
||||
!build/test/__snapshots__
|
||||
dist/
|
||||
|
||||
.idea/
|
||||
|
||||
@@ -17,9 +17,7 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Test", "test\Test.fsproj",
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lit.Test", "src\Lit.Test\Lit.Test.fsproj", "{1920B7A6-ABE1-49EB-B828-826A3ED20A75}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{ABCF0982-DCE7-493F-A9F2-FE6A6BA2EE22}"
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyApp", "sample\src\MyApp.fsproj", "{F6EB9FBF-2713-42EA-A701-88F2150EB988}"
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Sample", "sample\Sample.fsproj", "{316053DB-99B3-4D64-AB08-9CE563102A9E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -106,18 +104,18 @@ Global
|
||||
{1920B7A6-ABE1-49EB-B828-826A3ED20A75}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1920B7A6-ABE1-49EB-B828-826A3ED20A75}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1920B7A6-ABE1-49EB-B828-826A3ED20A75}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988}.Release|x86.Build.0 = Release|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{316053DB-99B3-4D64-AB08-9CE563102A9E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{207E7C81-549C-4CF5-830B-E681E383BDAB} = {02A16E23-0ECB-444D-B10B-245EAF6589DD}
|
||||
@@ -125,6 +123,5 @@ Global
|
||||
{0EBD5EA0-373F-4C63-B4D2-8C0681552554} = {02A16E23-0ECB-444D-B10B-245EAF6589DD}
|
||||
{EAD7C6EA-3264-4AA1-A267-DB83425E86B5} = {02A16E23-0ECB-444D-B10B-245EAF6589DD}
|
||||
{1920B7A6-ABE1-49EB-B828-826A3ED20A75} = {02A16E23-0ECB-444D-B10B-245EAF6589DD}
|
||||
{F6EB9FBF-2713-42EA-A701-88F2150EB988} = {ABCF0982-DCE7-493F-A9F2-FE6A6BA2EE22}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
7
build/test/__snapshots__/HookTest.snap.js
Normal file
7
build/test/__snapshots__/HookTest.snap.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/* @web/test-runner snapshot v1 */
|
||||
export const snapshots = {};
|
||||
|
||||
snapshots["counter"] =
|
||||
`%3Cdiv%3E%0A%20%20%20%20%0A%20%20%20%20%3Cp%3EF%23%20counter%3C%2Fp%3E%0A%20%20%20%20%3Cp%3EValue%3A%205%3C%2Fp%3E%0A%20%20%20%20%3Cbutton%3EIncrement%3C%2Fbutton%3E%0A%20%20%20%20%3Cbutton%3EDecrement%3C%2Fbutton%3E%0A%20%20%20%20%3C%2Fdiv%3E`;
|
||||
/* end snapshot counter */
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<body>
|
||||
<div id="app-container"></div>
|
||||
<script type="module" src="./out/Entry.js"></script>
|
||||
<script type="module" src="./build/sample/App.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -3,9 +3,9 @@
|
||||
"scripts": {
|
||||
"install": "dotnet tool restore",
|
||||
"publish": "dotnet fsi build.fsx publish",
|
||||
"test": "dotnet fable test --run web-test-runner test/**/*Test.fs.js --node-resolve",
|
||||
"test:watch": "dotnet fable watch test --run web-test-runner test/**/*Test.fs.js --node-resolve --watch",
|
||||
"start": "cd sample && dotnet fable watch src -o out --runFast vite"
|
||||
"test": "dotnet fable test -o build/test --run web-test-runner build/test/*Test.js --node-resolve",
|
||||
"test:watch": "dotnet fable watch test -o build/test --run web-test-runner build/test/*Test.js --node-resolve --watch",
|
||||
"start": "dotnet fable watch sample -o build/sample --runFast vite --open"
|
||||
},
|
||||
"dependencies": {
|
||||
"lit": "^2.0.0",
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,4 +1,4 @@
|
||||
module MyApp.Main
|
||||
module Sample.App
|
||||
|
||||
open Elmish
|
||||
open Lit
|
||||
@@ -5,6 +5,42 @@ open Fable.Core
|
||||
open Browser.Types
|
||||
open Elmish
|
||||
open Lit
|
||||
|
||||
module Helpers =
|
||||
type Time =
|
||||
| Hour of int
|
||||
| Minute of int
|
||||
| Second of int
|
||||
member this.Value =
|
||||
match this with
|
||||
| Hour n -> n
|
||||
| Second n -> n
|
||||
| Minute n -> n
|
||||
|
||||
member this.ClockPercentage =
|
||||
(float this.Value) / this.FullRound
|
||||
|
||||
member this.StrokeWidth =
|
||||
match this with
|
||||
| Hour _ | Minute _ -> 2
|
||||
| Second _ -> 1
|
||||
|
||||
member this.Length =
|
||||
match this with
|
||||
| Hour _ -> 25.
|
||||
| Minute _ -> 35.
|
||||
| Second _ -> 40.
|
||||
|
||||
member this.FullRound =
|
||||
match this with
|
||||
| Hour _ -> 12.
|
||||
| Second _ | Minute _ -> 60.
|
||||
|
||||
type System.DateTime with
|
||||
member this.AsHour = Hour this.Hour
|
||||
member this.AsMinute = Minute this.Minute
|
||||
member this.AsSecond = Second this.Second
|
||||
|
||||
open Helpers
|
||||
|
||||
type Model =
|
||||
@@ -1,4 +1,4 @@
|
||||
module MyApp.Components
|
||||
module Sample.Components
|
||||
|
||||
open Browser.Types
|
||||
open Lit
|
||||
@@ -3,15 +3,14 @@
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Types.fs" />
|
||||
<Compile Include="Helpers.fs" />
|
||||
<Compile Include="Clock.fs" />
|
||||
<Compile Include="Types.fs" />
|
||||
<Compile Include="Components.fs" />
|
||||
<Compile Include="Entry.fs" />
|
||||
<Compile Include="App.fs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Lit.React\Lit.React.fsproj" />
|
||||
<ProjectReference Include="..\..\src\Lit.Elmish\Lit.Elmish.fsproj" />
|
||||
<ProjectReference Include="..\src\Lit.React\Lit.React.fsproj" />
|
||||
<ProjectReference Include="..\src\Lit.Elmish\Lit.Elmish.fsproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- <PackageReference Include="Fable.Lit.Elmish" Version="1.0.0-rc-001" /> -->
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MyApp
|
||||
namespace Sample
|
||||
|
||||
type Model =
|
||||
{ Value: string
|
||||
@@ -1,35 +0,0 @@
|
||||
module Helpers
|
||||
|
||||
type Time =
|
||||
| Hour of int
|
||||
| Minute of int
|
||||
| Second of int
|
||||
member this.Value =
|
||||
match this with
|
||||
| Hour n -> n
|
||||
| Second n -> n
|
||||
| Minute n -> n
|
||||
|
||||
member this.ClockPercentage =
|
||||
(float this.Value) / this.FullRound
|
||||
|
||||
member this.StrokeWidth =
|
||||
match this with
|
||||
| Hour _ | Minute _ -> 2
|
||||
| Second _ -> 1
|
||||
|
||||
member this.Length =
|
||||
match this with
|
||||
| Hour _ -> 25.
|
||||
| Minute _ -> 35.
|
||||
| Second _ -> 40.
|
||||
|
||||
member this.FullRound =
|
||||
match this with
|
||||
| Hour _ -> 12.
|
||||
| Second _ | Minute _ -> 60.
|
||||
|
||||
type System.DateTime with
|
||||
member this.AsHour = Hour this.Hour
|
||||
member this.AsMinute = Minute this.Minute
|
||||
member this.AsSecond = Second this.Second
|
||||
60
src/Lit.Test/Expect.Elmish.fs
Normal file
60
src/Lit.Test/Expect.Elmish.fs
Normal file
@@ -0,0 +1,60 @@
|
||||
module Expect.Elmish
|
||||
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open Elmish
|
||||
open Lit
|
||||
open Expect.Dom
|
||||
|
||||
type Observable<'T>() =
|
||||
let mutable value: 'T option = None
|
||||
let listeners = Dictionary<Guid, IObserver<'T>>()
|
||||
member _.Trigger(v) =
|
||||
value <- Some v
|
||||
for l in listeners.Values do
|
||||
l.OnNext(v)
|
||||
interface IObservable<'T> with
|
||||
member _.Subscribe(w) =
|
||||
value |> Option.iter w.OnNext
|
||||
let g = Guid.NewGuid()
|
||||
listeners.Add(g, w)
|
||||
{ new IDisposable with
|
||||
member _.Dispose() = listeners.Remove(g) |> ignore }
|
||||
|
||||
type LazyDisposable() =
|
||||
let mutable _disposed = false
|
||||
let mutable _disposable: IDisposable option = None
|
||||
member _.Disposable with set(d: IDisposable) =
|
||||
match _disposed, _disposable with
|
||||
| false, Some _ -> failwith "Item was already assigned a disposable"
|
||||
| true, Some _ -> failwith "Item is already disposed"
|
||||
| false, None -> _disposable <- Some d
|
||||
| true, None ->
|
||||
_disposable <- Some d
|
||||
d.Dispose()
|
||||
member _.Dispose() =
|
||||
_disposed <- true
|
||||
_disposable |> Option.iter (fun d -> d.Dispose())
|
||||
|
||||
type IObservable<'T> with
|
||||
member obs.Await() =
|
||||
Promise.create(fun resolve _ ->
|
||||
let disp = LazyDisposable()
|
||||
disp.Disposable <- obs.Subscribe(fun v ->
|
||||
disp.Dispose()
|
||||
resolve v))
|
||||
|
||||
module Program =
|
||||
let runTest (program: Program<unit, 'model, 'msg, Lit.TemplateResult>) = promise {
|
||||
let obs = Observable<'model>()
|
||||
let! el = render_html $"<div></div>"
|
||||
|
||||
let setState model dispatch =
|
||||
Program.view program model dispatch |> Lit.render el.El
|
||||
obs.Trigger model
|
||||
|
||||
Program.withSetState setState program
|
||||
|> Program.run
|
||||
|
||||
return el, obs :> IObservable<_>
|
||||
}
|
||||
@@ -20,14 +20,15 @@ type JsError(message: string) =
|
||||
class end
|
||||
|
||||
[<AttachMembers>]
|
||||
type AssertionError<'T>(assertion: string, ?description: string, ?actual: 'T, ?expected: 'T) =
|
||||
type AssertionError<'T>(assertion: string, ?description: string, ?actual: 'T, ?expected: 'T, ?brief: bool) =
|
||||
inherit JsError(
|
||||
let brief = defaultArg brief false
|
||||
[
|
||||
"Expected " |> Some
|
||||
description |> Option.map (fun v -> $"'{v}' ")
|
||||
actual |> Option.map (fun v -> $"{quote v} ")
|
||||
if not brief then actual |> Option.map (fun v -> $"{quote v} ")
|
||||
$"to {assertion} " |> Some
|
||||
expected |> Option.map (fun v -> $"{quote v} ")
|
||||
if not brief then expected |> Option.map (fun v -> $"{quote v} ")
|
||||
] |> List.choose id |> String.concat ""
|
||||
)
|
||||
// Test runner requires these properties to be settable, not sure why
|
||||
@@ -35,8 +36,8 @@ type AssertionError<'T>(assertion: string, ?description: string, ?actual: 'T, ?e
|
||||
member val expected = expected with get, set
|
||||
|
||||
type AssertionError =
|
||||
static member Throw(assertion: string, ?description, ?actual: 'T, ?expected: 'T) =
|
||||
AssertionError.Throw(assertion, ?description=description, ?actual=actual, ?expected=expected)
|
||||
static member Throw(assertion: string, ?description, ?actual: 'T, ?expected: 'T, ?brief: bool) =
|
||||
AssertionError(assertion, ?description=description, ?actual=actual, ?expected=expected, ?brief=brief) |> throw
|
||||
|
||||
// TODO: String and collection assertions
|
||||
[<RequireQualifiedAccess>]
|
||||
@@ -81,6 +82,13 @@ module Expect =
|
||||
if condition actual then
|
||||
AssertionError.Throw("be false", description=msg)
|
||||
|
||||
let find (msg: string) (condition: 'T -> bool) (items: 'T seq) =
|
||||
items
|
||||
|> Seq.tryFind condition
|
||||
|> function
|
||||
| Some x -> x
|
||||
| None -> AssertionError.Throw("be found", description=msg)
|
||||
|
||||
let error (msg: string) (f: 'T -> 'Result) (actual: 'T) =
|
||||
try
|
||||
let _ = f actual
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Expect.fs" />
|
||||
<Compile Include="Expect.Dom.fs" />
|
||||
<Compile Include="Expect.Elmish.fs" />
|
||||
<Compile Include="WebTestRunner.fs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -31,11 +31,11 @@ module Expect =
|
||||
|
||||
let private cleanHtml (html: string) =
|
||||
// Lit inserts comments with different values every time, so remove them
|
||||
Regex(@"<\!--.*?-->").Replace(html, "").Trim()
|
||||
Regex(@"<\!--[\s\S]*?-->").Replace(html, "").Trim()
|
||||
|
||||
/// Compares the content string with the snapshot of the given name within the current file.
|
||||
/// If the snapshot doesn't exist or tests are run with `--update-snapshots` option the snapshot will just be saved/updated.
|
||||
let matchSnapshot (name: string) (content: string) = promise {
|
||||
let matchSnapshot (description: string) (name: string) (content: string) = promise {
|
||||
let! config = wtr.getSnapshotConfig()
|
||||
let! snapshot =
|
||||
if config.updateSnapshots then Promise.lift null
|
||||
@@ -47,15 +47,19 @@ module Expect =
|
||||
else
|
||||
// Don't use wtr.compareSnapshot because that will update the snapshot
|
||||
// without encoding the content even with a successful match
|
||||
return Expect.Expect.equal snapshot content
|
||||
return
|
||||
if not(snapshot = content) then
|
||||
// Snapshots can be large, so use `brief` argument to hide them in the error message
|
||||
// (Diffing should be displayed correctly)
|
||||
Expect.AssertionError.Throw("match snapshot", description=description, actual=content, expected=snapshot, brief=true)
|
||||
}
|
||||
|
||||
/// Compares `outerHML` of the element with the snapshot of the given name within the current file.
|
||||
/// If the snapshot doesn't exist or tests are run with `--update-snapshots` option the snapshot will just be saved/updated.
|
||||
let matchHtmlSnapshot (name: string) (el: HTMLElement) =
|
||||
el.outerHTML |> cleanHtml |> matchSnapshot name
|
||||
el.outerHTML |> cleanHtml |> matchSnapshot "outerHTML" name
|
||||
|
||||
/// Compares `shadowRoot.innerHTML` of the element with the snapshot of the given name within the current file.
|
||||
/// If the snapshot doesn't exist or tests are run with `--update-snapshots` option the snapshot will just be saved/updated.
|
||||
let matchShadowRootSnapshot (name: string) (el: Element) =
|
||||
el.shadowRoot.innerHTML |> cleanHtml |> matchSnapshot name
|
||||
el.shadowRoot.innerHTML |> cleanHtml |> matchSnapshot "shadowRoor" name
|
||||
|
||||
@@ -13,6 +13,8 @@ let Counter () =
|
||||
html
|
||||
$"""
|
||||
<div>
|
||||
<!-- Check snapshot can contain # char. See https://github.com/modernweb-dev/web/issues/1690 -->
|
||||
<p>F# counter</p>
|
||||
<p>Value: {value}</p>
|
||||
<button @click={Ev(fun _ -> value + 1 |> setValue)}>Increment</button>
|
||||
<button @click={Ev(fun _ -> value - 1 |> setValue)}>Decrement</button>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/* @web/test-runner snapshot v1 */
|
||||
export const snapshots = {};
|
||||
|
||||
snapshots["counter"] =
|
||||
`%3Cdiv%3E%0A%20%20%20%20%3Cp%3EValue%3A%205%3C%2Fp%3E%0A%20%20%20%20%3Cbutton%3EIncrement%3C%2Fbutton%3E%0A%20%20%20%20%3Cbutton%3EDecrement%3C%2Fbutton%3E%0A%20%20%20%20%3C%2Fdiv%3E`;
|
||||
/* end snapshot counter */
|
||||
|
||||
Reference in New Issue
Block a user