elm-pages 3.0.0-beta.14 → 3.0.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +1 -1
  2. package/codegen/elm-pages-codegen.js +66 -118
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +20 -20
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  9. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +5 -5
  10. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +21 -21
  11. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  12. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  13. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  14. package/generator/src/RouteBuilder.elm +23 -23
  15. package/generator/src/SharedTemplate.elm +2 -2
  16. package/generator/src/SiteConfig.elm +2 -2
  17. package/generator/src/cli.js +2 -2
  18. package/generator/src/compatibility-key.js +1 -1
  19. package/generator/src/error-formatter.js +7 -3
  20. package/generator/src/render.js +6 -15
  21. package/generator/src/request-cache.js +34 -4
  22. package/generator/static-code/hmr.js +16 -2
  23. package/package.json +1 -1
  24. package/src/ApiRoute.elm +13 -16
  25. package/src/BackendTask/Env.elm +11 -8
  26. package/src/BackendTask/File.elm +49 -10
  27. package/src/BackendTask/Glob.elm +6 -6
  28. package/src/BackendTask/Http.elm +49 -13
  29. package/src/BackendTask/Port.elm +59 -47
  30. package/src/BackendTask.elm +8 -22
  31. package/src/FatalError.elm +101 -0
  32. package/src/Form.elm +3 -2
  33. package/src/Internal/ApiRoute.elm +5 -5
  34. package/src/Pages/Generate.elm +300 -103
  35. package/src/Pages/Internal/FatalError.elm +5 -0
  36. package/src/Pages/Internal/Platform/Cli.elm +21 -41
  37. package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
  38. package/src/Pages/Internal/Platform/GeneratorApplication.elm +24 -48
  39. package/src/Pages/Internal/Platform/StaticResponses.elm +18 -31
  40. package/src/Pages/Internal/Script.elm +2 -2
  41. package/src/Pages/Manifest.elm +2 -2
  42. package/src/Pages/ProgramConfig.elm +7 -7
  43. package/src/Pages/Script.elm +4 -4
  44. package/src/Pages/SiteConfig.elm +2 -2
  45. package/src/Pages/StaticHttpRequest.elm +1 -23
  46. package/src/Server/Request.elm +3 -2
  47. package/src/Exception.elm +0 -37
  48. package/src/MultiDict.elm +0 -49
  49. package/src/PairingHeap.elm +0 -137
  50. package/src/Parser/Extra/String.elm +0 -33
  51. package/src/Parser/Extra.elm +0 -69
  52. package/src/ProgramTest/ComplexQuery.elm +0 -360
  53. package/src/ProgramTest/EffectSimulation.elm +0 -122
  54. package/src/ProgramTest/Failure.elm +0 -367
  55. package/src/ProgramTest/HtmlHighlighter.elm +0 -116
  56. package/src/ProgramTest/HtmlParserHacks.elm +0 -58
  57. package/src/ProgramTest/HtmlRenderer.elm +0 -73
  58. package/src/ProgramTest/Program.elm +0 -30
  59. package/src/ProgramTest/StringLines.elm +0 -26
  60. package/src/ProgramTest/TestHtmlHacks.elm +0 -132
  61. package/src/ProgramTest/TestHtmlParser.elm +0 -201
  62. package/src/ProgramTest.elm +0 -2339
  63. package/src/Query/Extra.elm +0 -55
  64. package/src/SimulatedEffect/Cmd.elm +0 -69
  65. package/src/SimulatedEffect/Http.elm +0 -330
  66. package/src/SimulatedEffect/Navigation.elm +0 -69
  67. package/src/SimulatedEffect/Ports.elm +0 -62
  68. package/src/SimulatedEffect/Process.elm +0 -24
  69. package/src/SimulatedEffect/Sub.elm +0 -48
  70. package/src/SimulatedEffect/Task.elm +0 -252
  71. package/src/SimulatedEffect/Time.elm +0 -25
  72. package/src/SimulatedEffect.elm +0 -42
  73. package/src/String/Extra.elm +0 -6
  74. package/src/Test/Http.elm +0 -145
  75. package/src/TestResult.elm +0 -35
  76. package/src/TestState.elm +0 -305
  77. package/src/Url/Extra.elm +0 -100
  78. package/src/Vendored/Diff.elm +0 -321
  79. package/src/Vendored/Failure.elm +0 -217
  80. package/src/Vendored/FormatMonochrome.elm +0 -44
  81. package/src/Vendored/Highlightable.elm +0 -53
@@ -1,2339 +0,0 @@
1
- module ProgramTest exposing
2
- ( ProgramTest, start
3
- , createSandbox, createElement, createDocument, createApplication, createWorker
4
- , ProgramDefinition
5
- , withBaseUrl, withJsonStringFlags
6
- , withSimulatedEffects, SimulatedEffect, SimulatedTask
7
- , withSimulatedSubscriptions, SimulatedSub
8
- , done
9
- , expectViewHas, expectViewHasNot, expectView
10
- , ensureViewHas, ensureViewHasNot, ensureView
11
- , clickButton, clickLink
12
- , fillIn, fillInTextarea
13
- , check, selectOption
14
- , simulateDomEvent
15
- , within
16
- , expectHttpRequestWasMade, expectHttpRequest, expectHttpRequests
17
- , ensureHttpRequestWasMade, ensureHttpRequest, ensureHttpRequests
18
- , simulateHttpOk, simulateHttpResponse, simulateHttpResponseAdvanced
19
- , advanceTime
20
- , expectOutgoingPortValues, ensureOutgoingPortValues
21
- , simulateIncomingPort
22
- , expectPageChange, expectBrowserUrl, expectBrowserHistory
23
- , ensureBrowserUrl, ensureBrowserHistory
24
- , routeChange
25
- , update
26
- , expectModel
27
- , expectLastEffect, ensureLastEffect
28
- , simulateLastEffect
29
- , fail, createFailed
30
- , getOutgoingPortValues
31
- )
32
-
33
- {-| A `ProgramTest` simulates the execution of an Elm program
34
- enabling you write high-level tests for your program.
35
- (Testing your programs at this level
36
- provides test coverage that is resilient even to drastic refactorings of your application architecture,
37
- and encourages tests that make it clear how end-users and external services will interact with your program.)
38
-
39
- This module allows you to interact with your program by simulating
40
- user interactions and external events (like HTTP responses and ports),
41
- and making assertions about the HTML it renders and the external requests it makes.
42
-
43
- - [Guide for upgrading from elm-program-test 2.x to 3.x](https://elm-program-test.netlify.com/upgrade-3.0.0.html)
44
-
45
-
46
- ## Documentation index
47
-
48
- The list below is an index into the API documentation for the
49
- assertion and simulation functions relevant to each topic:
50
-
51
- - creating tests: [creating](#creating-program-definitions) — [starting](#start) — [options](#options)
52
- - **HTML**: [assertions](#inspecting-html) — [simulating user input](#simulating-user-input)
53
- - **HTTP**: [assertions](#inspecting-http-requests) — [simulating responses](#simulating-http-responses)
54
- - **time**: [simulating the passing of time](#simulating-time)
55
- - **ports**: [assertions](#inspecting-outgoing-ports) — [simulating incoming ports](#simulating-incoming-ports)
56
- - **browser**: [assertions](#browser-assertions) — [simulating](#simulating-browser-interactions)
57
-
58
-
59
- ## Getting started
60
-
61
- For a more detailed explanation of how to get started,
62
- see the elm-program-test guidebooks
63
- (the best one to start with is “Testing programs with interactive views”):
64
-
65
- - [Testing programs with interactive views](https://elm-program-test.netlify.com//html.html) —
66
- shows an example of test-driving adding form validation to an Elm program
67
- - [Testing programs with Cmds](https://elm-program-test.netlify.com/cmds.html) — shows testing a program
68
- that uses `Http.get` and `Http.post`
69
- - [Testing programs with ports](https://elm-program-test.netlify.com/ports.html) — shows testing a program
70
- that uses ports to interface with JavaScript
71
-
72
-
73
- # Creating
74
-
75
- @docs ProgramTest, start
76
-
77
-
78
- ## Creating program definitions
79
-
80
- A `ProgramDefinition` (required to create a `ProgramTest` with [`start`](#start))
81
- can be created with one of the following functions that parallel
82
- the functions in [`elm/browser`](https://package.elm-lang.org/packages/elm/browser/latest/Browser) for creating programs.
83
-
84
- @docs createSandbox, createElement, createDocument, createApplication, createWorker
85
- @docs ProgramDefinition
86
-
87
-
88
- ## Options
89
-
90
- The following functions allow you to configure your
91
- `ProgramDefinition` before starting it with [`start`](#start).
92
-
93
- @docs withBaseUrl, withJsonStringFlags
94
-
95
- @docs withSimulatedEffects, SimulatedEffect, SimulatedTask
96
- @docs withSimulatedSubscriptions, SimulatedSub
97
-
98
-
99
- ## Ending a test
100
-
101
- @docs done
102
-
103
-
104
- # Inspecting and interacting with HTML
105
-
106
-
107
- ## Inspecting HTML
108
-
109
- @docs expectViewHas, expectViewHasNot, expectView
110
- @docs ensureViewHas, ensureViewHasNot, ensureView
111
-
112
-
113
- ## Simulating user input
114
-
115
- @docs clickButton, clickLink
116
- @docs fillIn, fillInTextarea
117
- @docs check, selectOption
118
-
119
-
120
- ## Simulating user input (advanced)
121
-
122
- @docs simulateDomEvent
123
- @docs within
124
-
125
-
126
- # Inspecting and simulating HTTP requests and responses
127
-
128
-
129
- # Inspecting HTTP requests
130
-
131
- @docs expectHttpRequestWasMade, expectHttpRequest, expectHttpRequests
132
- @docs ensureHttpRequestWasMade, ensureHttpRequest, ensureHttpRequests
133
-
134
-
135
- ## Simulating HTTP responses
136
-
137
- @docs simulateHttpOk, simulateHttpResponse, simulateHttpResponseAdvanced
138
-
139
-
140
- # Simulating time
141
-
142
- @docs advanceTime
143
-
144
-
145
- # Inspecting and simulating ports
146
-
147
-
148
- ## Inspecting outgoing ports
149
-
150
- @docs expectOutgoingPortValues, ensureOutgoingPortValues
151
-
152
-
153
- ## Simulating incoming ports
154
-
155
- @docs simulateIncomingPort
156
-
157
-
158
- # Browser navigation
159
-
160
-
161
- ## Browser assertions
162
-
163
- @docs expectPageChange, expectBrowserUrl, expectBrowserHistory
164
- @docs ensureBrowserUrl, ensureBrowserHistory
165
-
166
-
167
- ## Simulating browser interactions
168
-
169
- @docs routeChange
170
-
171
-
172
- # Low-level functions
173
-
174
- You should avoid the functions below when possible,
175
- but you may find them useful to test things that are not yet directly supported by elm-program-test.
176
-
177
-
178
- ## Low-level functions for Msgs and Models
179
-
180
- @docs update
181
- @docs expectModel
182
-
183
-
184
- ## Low-level functions for effects
185
-
186
- @docs expectLastEffect, ensureLastEffect
187
- @docs simulateLastEffect
188
-
189
-
190
- ## Custom assertions
191
-
192
- These functions may be useful if you are writing your own custom assertion functions.
193
-
194
- @docs fail, createFailed
195
- @docs getOutgoingPortValues
196
-
197
- -}
198
-
199
- import Browser
200
- import Dict exposing (Dict)
201
- import Expect exposing (Expectation)
202
- import Html exposing (Html)
203
- import Html.Attributes exposing (attribute)
204
- import Http
205
- import Json.Decode
206
- import Json.Encode
207
- import List.Extra
208
- import MultiDict
209
- import ProgramTest.ComplexQuery as ComplexQuery exposing (ComplexQuery)
210
- import ProgramTest.EffectSimulation as EffectSimulation exposing (EffectSimulation)
211
- import ProgramTest.Failure as Failure exposing (Failure(..))
212
- import ProgramTest.Program as Program exposing (Program)
213
- import Result.Extra
214
- import SimulatedEffect exposing (SimulatedEffect, SimulatedSub, SimulatedTask)
215
- import String.Extra
216
- import Test.Html.Event
217
- import Test.Html.Query as Query
218
- import Test.Html.Selector as Selector exposing (Selector)
219
- import Test.Http
220
- import Test.Runner
221
- import TestResult exposing (TestResult)
222
- import TestState exposing (TestState)
223
- import Url exposing (Url)
224
-
225
-
226
- {-| A `ProgramTest` represents an Elm program,
227
- a current state for that program,
228
- information about external effects that have been produced by the program (such as pending HTTP requests, values sent to outgoing ports, etc),
229
- and a log of any errors that have occurred while simulating interaction with the program.
230
-
231
- - To create a `ProgramTest`, see the `create*` functions below.
232
- - To advance the state of a `ProgramTest`, see [Simulating user input](#simulating-user-input), or the many simulate functions in this module.
233
- - To assert on the resulting state of a `ProgramTest`, see the many `expect*` functions in this module.
234
-
235
- -}
236
- type ProgramTest model msg effect
237
- = Created
238
- { program : Program model msg effect (SimulatedSub msg)
239
- , state : TestResult model msg effect
240
- }
241
- | FailedToCreate Failure
242
-
243
-
244
- andThen :
245
- (Program model msg effect (SimulatedSub msg) -> TestState model msg effect -> Result Failure (TestState model msg effect))
246
- -> ProgramTest model msg effect
247
- -> ProgramTest model msg effect
248
- andThen f programTest =
249
- case programTest of
250
- Created created ->
251
- Created
252
- { created
253
- | state = TestResult.andThen (f created.program) created.state
254
- }
255
-
256
- FailedToCreate failure ->
257
- FailedToCreate failure
258
-
259
-
260
- toFailure : ProgramTest model msg effect -> Maybe Failure
261
- toFailure programTest =
262
- case programTest of
263
- Created created ->
264
- case created.state of
265
- Err f ->
266
- Just f.reason
267
-
268
- Ok _ ->
269
- Nothing
270
-
271
- FailedToCreate f ->
272
- Just f
273
-
274
-
275
- type alias TestLog model msg =
276
- { view : model -> Html msg
277
- , history : List model
278
- }
279
-
280
-
281
- type alias ProgramOptions model msg effect =
282
- { baseUrl : Maybe Url
283
- , deconstructEffect : Maybe (effect -> SimulatedEffect msg)
284
- , subscriptions : Maybe (model -> SimulatedSub msg)
285
- }
286
-
287
-
288
- emptyOptions : ProgramOptions model msg effect
289
- emptyOptions =
290
- { baseUrl = Nothing
291
- , deconstructEffect = Nothing
292
- , subscriptions = Nothing
293
- }
294
-
295
-
296
- {-| Represents an unstarted program test.
297
- Use [`start`](#start) to start the program being tested.
298
- -}
299
- type ProgramDefinition flags model msg effect
300
- = ProgramDefinition (ProgramOptions model msg effect) (Maybe Url -> flags -> ProgramOptions model msg effect -> ProgramTest model msg effect)
301
-
302
-
303
- createHelper :
304
- { init : ( model, effect )
305
- , update : msg -> model -> ( model, effect )
306
- , view : model -> Html msg
307
- , onUrlRequest : Maybe (Browser.UrlRequest -> msg)
308
- , onUrlChange : Maybe (Url -> msg)
309
- }
310
- -> ProgramOptions model msg effect
311
- -> ProgramTest model msg effect
312
- createHelper program options =
313
- let
314
- program_ =
315
- { update = program.update
316
- , view = program.view
317
- , onUrlRequest = program.onUrlRequest
318
- , onUrlChange = program.onUrlChange
319
- , subscriptions = options.subscriptions
320
- , withinFocus = identity
321
- }
322
-
323
- ( newModel, newEffect ) =
324
- program.init
325
- in
326
- Created
327
- { program = program_
328
- , state =
329
- Ok
330
- -- TODO: move to TestState.init after pulling deconstructEffect out of EffectSimulation
331
- { currentModel = newModel
332
- , lastEffect = newEffect
333
- , navigation =
334
- case options.baseUrl of
335
- Nothing ->
336
- Nothing
337
-
338
- Just baseUrl ->
339
- Just
340
- { currentLocation = baseUrl
341
- , browserHistory = []
342
- }
343
- , effectSimulation = Maybe.map EffectSimulation.init options.deconstructEffect
344
- }
345
- }
346
- |> andThen
347
- (\_ ->
348
- TestState.queueEffect program_ newEffect
349
- >> Result.andThen (TestState.drain program_)
350
- )
351
-
352
-
353
- {-| Creates a `ProgramDefinition` from the parts of a [`Browser.sandbox`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox) program.
354
-
355
- See other `create*` functions below if the program you want to test does not use `Browser.sandbox`.
356
-
357
- -}
358
- createSandbox :
359
- { init : model
360
- , view : model -> Html msg
361
- , update : msg -> model -> model
362
- }
363
- -> ProgramDefinition () model msg ()
364
- createSandbox program =
365
- ProgramDefinition emptyOptions <|
366
- \_ () ->
367
- createHelper
368
- { init = ( program.init, () )
369
- , update = \msg model -> ( program.update msg model, () )
370
- , view = program.view
371
- , onUrlRequest = Nothing
372
- , onUrlChange = Nothing
373
- }
374
-
375
-
376
- {-| Creates a `ProgramTest` from the parts of a [`Platform.worker`](https://package.elm-lang.org/packages/elm/core/latest/Platform#worker) program.
377
-
378
- See other `create*` functions if the program you want to test does not use `Platform.worker`.
379
-
380
- If your program has subscriptions that you want to simulate, see [`withSimulatedSubscriptions`](#withSimulatedSubscriptions).
381
-
382
- -}
383
- createWorker :
384
- { init : flags -> ( model, effect )
385
- , update : msg -> model -> ( model, effect )
386
- }
387
- -> ProgramDefinition flags model msg effect
388
- createWorker program =
389
- ProgramDefinition emptyOptions <|
390
- \_ flags ->
391
- createHelper
392
- { init = program.init flags
393
- , update = program.update
394
- , view = \_ -> Html.text "** Programs created with ProgramTest.createWorker do not have a view. Use ProgramTest.createElement instead if you meant to provide a view function. **"
395
- , onUrlRequest = Nothing
396
- , onUrlChange = Nothing
397
- }
398
-
399
-
400
- {-| Creates a `ProgramTest` from the parts of a [`Browser.element`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#element) program.
401
-
402
- See other `create*` functions below if the program you want to test does not use `Browser.element`.
403
-
404
- If your program has subscriptions that you want to simulate, see [`withSimulatedSubscriptions`](#withSimulatedSubscriptions).
405
-
406
- -}
407
- createElement :
408
- { init : flags -> ( model, effect )
409
- , view : model -> Html msg
410
- , update : msg -> model -> ( model, effect )
411
- }
412
- -> ProgramDefinition flags model msg effect
413
- createElement program =
414
- ProgramDefinition emptyOptions <|
415
- \_ flags ->
416
- createHelper
417
- { init = program.init flags
418
- , update = program.update
419
- , view = program.view
420
- , onUrlRequest = Nothing
421
- , onUrlChange = Nothing
422
- }
423
-
424
-
425
- {-| Starts the given test program by initializing it with the given flags.
426
-
427
- If your program uses `Json.Encode.Value` as its flags type,
428
- you may find [`withJsonStringFlags`](#withJsonStringFlags) useful.
429
-
430
- -}
431
- start : flags -> ProgramDefinition flags model msg effect -> ProgramTest model msg effect
432
- start flags (ProgramDefinition options program) =
433
- program options.baseUrl flags options
434
-
435
-
436
- {-| Sets the initial browser URL
437
-
438
- You must set this when using `createApplication`,
439
- or when using [`clickLink`](#clickLink) and [`expectPageChange`](#expectPageChange)
440
- to simulate a user clicking a link with relative URL.
441
-
442
- -}
443
- withBaseUrl : String -> ProgramDefinition flags model msg effect -> ProgramDefinition flags model msg effect
444
- withBaseUrl baseUrl (ProgramDefinition options program) =
445
- case Url.fromString baseUrl of
446
- Nothing ->
447
- ProgramDefinition options
448
- (\_ _ _ ->
449
- FailedToCreate (InvalidLocationUrl "withBaseUrl" baseUrl)
450
- )
451
-
452
- Just url ->
453
- ProgramDefinition { options | baseUrl = Just url } program
454
-
455
-
456
- {-| Provides a convenient way to provide flags for a program that decodes flags from JSON.
457
- By providing the JSON decoder, you can then provide the flags as a JSON string when calling
458
- [`start`](#start).
459
- -}
460
- withJsonStringFlags :
461
- Json.Decode.Decoder flags
462
- -> ProgramDefinition flags model msg effect
463
- -> ProgramDefinition String model msg effect
464
- withJsonStringFlags decoder (ProgramDefinition options program) =
465
- ProgramDefinition options <|
466
- \location json ->
467
- case Json.Decode.decodeString decoder json of
468
- Ok flags ->
469
- program location flags
470
-
471
- Err message ->
472
- \_ ->
473
- FailedToCreate (InvalidFlags "withJsonStringFlags" (Json.Decode.errorToString message))
474
-
475
-
476
- {-| This allows you to provide a function that lets `ProgramTest` simulate effects that would become `Cmd`s and `Task`s
477
- when your app runs in production
478
- (this enables you to use [`simulateHttpResponse`](#simulateHttpResponse), [`advanceTime`](#advanceTime), etc.).
479
- For a detailed explanation and example of how to set up tests that use simulated effects,
480
- see the [“Testing programs with Cmds” guidebook](https://elm-program-test.netlify.com/cmds.html).
481
-
482
- You only need to use this if you need to simulate [HTTP requests](#simulating-http-responses),
483
- [outgoing ports](#expectOutgoingPortValues),
484
- or the [passing of time](#simulating-time).
485
-
486
- See the `SimulatedEffect.*` modules in this package for functions that you can use to implement
487
- the required `effect -> SimulatedEffect msg` function for your `effect` type.
488
-
489
- -}
490
- withSimulatedEffects :
491
- (effect -> SimulatedEffect msg)
492
- -> ProgramDefinition flags model msg effect
493
- -> ProgramDefinition flags model msg effect
494
- withSimulatedEffects fn (ProgramDefinition options program) =
495
- ProgramDefinition { options | deconstructEffect = Just fn } program
496
-
497
-
498
- {-| This allows you to provide a function that lets `ProgramTest` simulate subscriptions that would be `Sub`s
499
- when your app runs in production
500
- (this enables you to use [`simulateIncomingPort`](#simulateIncomingPort), etc.).
501
- You only need to use this if you need to simulate subscriptions in your test.
502
- For a detailed explanation and example of how to set up tests that use simulated subscriptions,
503
- see the [“Testing programs with ports” guidebook](https://elm-program-test.netlify.com/ports.html).
504
-
505
- The function you provide should be similar to your program's `subscriptions` function
506
- but return `SimulatedSub`s instead of `Sub`s.
507
- See the `SimulatedEffect.*` modules in this package for functions that you can use to implement
508
- the required `model -> SimulatedSub msg` function.
509
-
510
- -}
511
- withSimulatedSubscriptions :
512
- (model -> SimulatedSub msg)
513
- -> ProgramDefinition flags model msg effect
514
- -> ProgramDefinition flags model msg effect
515
- withSimulatedSubscriptions fn (ProgramDefinition options program) =
516
- ProgramDefinition { options | subscriptions = Just fn } program
517
-
518
-
519
- {-| Creates a `ProgramTest` from the parts of a [`Browser.document`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#document) program.
520
-
521
- See other `create*` functions if the program you want to test does not use `Browser.document`.
522
-
523
- If your program has subscriptions that you want to simulate, see [`withSimulatedSubscriptions`](#withSimulatedSubscriptions).
524
-
525
- -}
526
- createDocument :
527
- { init : flags -> ( model, effect )
528
- , view : model -> Browser.Document msg
529
- , update : msg -> model -> ( model, effect )
530
- }
531
- -> ProgramDefinition flags model msg effect
532
- createDocument program =
533
- ProgramDefinition emptyOptions <|
534
- \_ flags ->
535
- createHelper
536
- { init = program.init flags
537
- , update = program.update
538
- , view = \model -> Html.node "body" [] (program.view model).body
539
- , onUrlRequest = Nothing
540
- , onUrlChange = Nothing
541
- }
542
-
543
-
544
- {-| Creates a `ProgramTest` from the parts of a [`Browser.application`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#application) program.
545
-
546
- See other `create*` functions if the program you want to test does not use `Browser.application`.
547
-
548
- If your program has subscriptions that you want to simulate, see [`withSimulatedSubscriptions`](#withSimulatedSubscriptions).
549
-
550
- Note that Elm currently does not provide any way to create a [`Browser.Navigation.Key`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation#Key) in tests, so this function uses `()` as the key type instead.
551
- For an example of how to test such a program, see
552
- [NavigationKeyExample.elm](https://github.com/avh4/elm-program-test/blob/main/examples/src/NavigationKeyExample.elm)
553
- and [NavigationKeyExampleTest.elm](https://github.com/avh4/elm-program-test/blob/main/examples/tests/NavigationKeyExampleTest.elm).
554
-
555
- -}
556
- createApplication :
557
- { init : flags -> Url -> () -> ( model, effect )
558
- , view : model -> Browser.Document msg
559
- , update : msg -> model -> ( model, effect )
560
- , onUrlRequest : Browser.UrlRequest -> msg
561
- , onUrlChange : Url -> msg
562
- }
563
- -> ProgramDefinition flags model msg effect
564
- createApplication program =
565
- ProgramDefinition emptyOptions <|
566
- \location flags ->
567
- case location of
568
- Nothing ->
569
- \_ ->
570
- FailedToCreate (NoBaseUrl "createApplication" "")
571
-
572
- Just url ->
573
- createHelper
574
- { init = program.init flags url ()
575
- , update = program.update
576
- , view = \model -> Html.node "body" [] (program.view model).body
577
- , onUrlRequest = Just program.onUrlRequest
578
- , onUrlChange = Just program.onUrlChange
579
- }
580
-
581
-
582
- {-| This represents an effect that elm-program-test is able to simulate.
583
- When using [`withSimulatedEffects`](#withSimulatedEffects) you will provide a function that can translate
584
- your program's effects into `SimulatedEffect`s.
585
- (If you do not use `withSimulatedEffects`,
586
- then `ProgramTest` will not simulate any effects for you.)
587
-
588
- You can create `SimulatedEffect`s using the following modules,
589
- which parallel the modules your real program would use to create `Cmd`s and `Task`s:
590
-
591
- - [`SimulatedEffect.Http`](SimulatedEffect-Http) (parallels `Http` from `elm/http`)
592
- - [`SimulatedEffect.Cmd`](SimulatedEffect-Cmd) (parallels `Platform.Cmd` from `elm/core`)
593
- - [`SimulatedEffect.Navigation`](SimulatedEffect-Navigation) (parallels `Browser.Navigation` from `elm/browser`)
594
- - [`SimulatedEffect.Ports`](SimulatedEffect-Ports) (parallels the `port` keyword)
595
- - [`SimulatedEffect.Task`](SimulatedEffect-Task) (parallels `Task` from `elm/core`)
596
- - [`SimulatedEffect.Process`](SimulatedEffect-Process) (parallels `Process` from `elm/core`)
597
- - [`SimulatedEffect.Time`](SimulatedEffect-Time) (parallels `Time` from `elm/time`)
598
-
599
- -}
600
- type alias SimulatedEffect msg =
601
- SimulatedEffect.SimulatedEffect msg
602
-
603
-
604
- {-| Similar to `SimulatedEffect`, but represents a `Task` instead of a `Cmd`.
605
- -}
606
- type alias SimulatedTask x a =
607
- SimulatedEffect.SimulatedTask x a
608
-
609
-
610
- {-| This represents a subscription that elm-program-test is able to simulate.
611
- When using [`withSimulatedSubscriptions`](#withSimulatedSubscriptions) you will provide
612
- a function that is similar to your program's `subscriptions` function but that
613
- returns `SimulatedSub`s instead `Sub`s.
614
- (If you do not use `withSimulatedSubscriptions`,
615
- then `ProgramTest` will not simulate any subscriptions for you.)
616
-
617
- You can create `SimulatedSub`s using the following modules:
618
-
619
- - [`SimulatedEffect.Ports`](SimulatedEffect-Ports) (parallels the `port` keyword)
620
-
621
- -}
622
- type alias SimulatedSub msg =
623
- SimulatedEffect.SimulatedSub msg
624
-
625
-
626
- {-| Advances the state of the `ProgramTest` by applying the given `msg` to your program's update function
627
- (provided when you created the `ProgramTest`).
628
-
629
- This can be used to simulate events that can only be triggered by [commands (`Cmd`) and subscriptions (`Sub`)](https://guide.elm-lang.org/architecture/effects/)
630
- (i.e., that cannot be triggered by user interaction with the view).
631
-
632
- NOTE: When possible, you should prefer [Simulating user input](#simulating-user-input),
633
- [Simulating HTTP responses](#simulating-http-responses),
634
- or (if neither of those support what you need) [`simulateLastEffect`](#simulateLastEffect),
635
- as doing so will make your tests more resilient to changes in your program's implementation details.
636
-
637
- -}
638
- update : msg -> ProgramTest model msg effect -> ProgramTest model msg effect
639
- update msg =
640
- andThen (TestState.update msg)
641
-
642
-
643
- {-| DEPRECATED: use `simulateComplexQuery` instead
644
- -}
645
- simulateHelper :
646
- String
647
- -> (Query.Single msg -> Query.Single msg)
648
- -> ( String, Json.Encode.Value )
649
- -> Program model msg effect sub
650
- -> TestState model msg effect
651
- -> Result Failure (TestState model msg effect)
652
- simulateHelper functionDescription findTarget event program state =
653
- let
654
- targetQuery =
655
- Program.renderView program state.currentModel
656
- |> findTarget
657
- in
658
- -- First check the target so we can give a better error message if it doesn't exist
659
- case
660
- targetQuery
661
- |> Query.has []
662
- |> Test.Runner.getFailureReason
663
- of
664
- Just reason ->
665
- Err (SimulateFailedToFindTarget functionDescription reason.description)
666
-
667
- Nothing ->
668
- -- Try to simulate the event, now that we know the target exists
669
- case
670
- targetQuery
671
- |> Test.Html.Event.simulate event
672
- |> Test.Html.Event.toResult
673
- of
674
- Err message ->
675
- Err (SimulateFailed functionDescription message)
676
-
677
- Ok msg ->
678
- TestState.update msg program state
679
-
680
-
681
- {-| **PRIVATE** helper for simulating events on input elements with associated labels.
682
-
683
- NOTE: Currently, this function requires that you also provide the field id
684
- (which must match both the `id` attribute of the target `input` element,
685
- and the `for` attribute of the `label` element).
686
- After [eeue56/elm-html-test#52](https://github.com/eeue56/elm-html-test/issues/52) is resolved,
687
- a future release of this package will remove the `fieldId` parameter.
688
-
689
- -}
690
- simulateLabeledInputHelper : String -> String -> String -> Bool -> List Selector -> ( String, Json.Encode.Value ) -> ProgramTest model msg effect -> ProgramTest model msg effect
691
- simulateLabeledInputHelper functionDescription fieldId label allowTextArea additionalInputSelectors event =
692
- let
693
- associatedLabel : List Selector
694
- associatedLabel =
695
- [ Selector.tag "label"
696
- , Selector.attribute (Html.Attributes.for fieldId)
697
- , Selector.text label
698
- ]
699
-
700
- checks =
701
- if allowTextArea then
702
- checks_ "input" ++ checks_ "textarea"
703
-
704
- else
705
- checks_ "input"
706
-
707
- checks_ : String -> List ( String, ComplexQuery (Query.Single msg) -> ComplexQuery msg )
708
- checks_ inputTag =
709
- if fieldId == "" then
710
- [ ( "<" ++ inputTag ++ "> with parent <label>"
711
- , ComplexQuery.find (Just "find label")
712
- [ "label" ]
713
- [ Selector.tag "label"
714
- , Selector.containing [ Selector.text label ]
715
- ]
716
- >> ComplexQuery.find Nothing
717
- [ inputTag ]
718
- [ Selector.tag inputTag ]
719
- >> ComplexQuery.simulate event
720
- )
721
- , ( "<" ++ inputTag ++ "> with aria-label"
722
- , ComplexQuery.find Nothing
723
- [ inputTag ]
724
- [ Selector.tag inputTag
725
- , Selector.attribute (attribute "aria-label" label)
726
- ]
727
- >> ComplexQuery.simulate event
728
- )
729
- ]
730
-
731
- else
732
- [ ( "<" ++ inputTag ++ "> associated to <label> by id"
733
- , ComplexQuery.check "check label exists"
734
- (ComplexQuery.find Nothing [ "label" ] associatedLabel)
735
- >> ComplexQuery.find (Just ("find " ++ inputTag))
736
- [ inputTag ]
737
- (List.concat
738
- [ [ Selector.tag inputTag
739
- , Selector.id fieldId
740
- ]
741
- , additionalInputSelectors
742
- ]
743
- )
744
- >> ComplexQuery.simulate event
745
- )
746
- , ( "<" ++ inputTag ++ "> with aria-label and id"
747
- , ComplexQuery.find Nothing
748
- [ inputTag ]
749
- [ Selector.tag inputTag
750
- , Selector.id fieldId
751
- , Selector.attribute (attribute "aria-label" label)
752
- ]
753
- >> ComplexQuery.simulate event
754
- )
755
- ]
756
- in
757
- simulateComplexQuery functionDescription
758
- (ComplexQuery.exactlyOneOf
759
- ("Expected one of the following to exist and have an " ++ String.Extra.escape ("on" ++ Tuple.first event) ++ " handler")
760
- checks
761
- )
762
-
763
-
764
- runComplexQuery :
765
- String
766
- -> (ComplexQuery (Query.Single msg) -> ComplexQuery a)
767
- ->
768
- (a
769
- -> Program model msg effect (SimulatedSub msg)
770
- -> TestState model msg effect
771
- -> Result Failure (TestState model msg effect)
772
- )
773
- -> ProgramTest model msg effect
774
- -> ProgramTest model msg effect
775
- runComplexQuery functionName complexQuery fn =
776
- andThen <|
777
- \program state ->
778
- let
779
- view =
780
- Program.renderView program state.currentModel
781
- in
782
- case ComplexQuery.run (complexQuery (ComplexQuery.succeed view)) of
783
- ( _, Ok a ) ->
784
- fn a program state
785
-
786
- ( highlight, Err queryFailure ) ->
787
- Err (ViewAssertionFailed ("ProgramTest." ++ functionName) (Html.map (\_ -> ()) (program.view state.currentModel)) highlight queryFailure)
788
-
789
-
790
- {-| TODO: have other internal functions use this to have more consistent error message.
791
- -}
792
- simulateComplexQuery : String -> (ComplexQuery (Query.Single msg) -> ComplexQuery msg) -> ProgramTest model msg effect -> ProgramTest model msg effect
793
- simulateComplexQuery functionName complexQuery =
794
- runComplexQuery functionName complexQuery TestState.update
795
-
796
-
797
- assertComplexQuery : String -> (ComplexQuery (Query.Single msg) -> ComplexQuery ignored) -> ProgramTest model msg effect -> ProgramTest model msg effect
798
- assertComplexQuery functionName complexQuery =
799
- runComplexQuery functionName complexQuery (\_ _ state -> Ok state)
800
-
801
-
802
- {-| Simulates a custom DOM event.
803
-
804
- NOTE: If there is another, more specific function (see [“Simulating user input”](#simulating-user-input))
805
- that does what you want, prefer that instead, as you will get the benefit of better error messages.
806
-
807
- The parameters are:
808
-
809
- 1. A function to find the HTML element that responds to the event
810
- (typically this will be a call to `Test.Html.Query.find [ ...some selector... ]`)
811
- 2. The event to simulate
812
- (see [Test.Html.Event "Event Builders"](https://package.elm-lang.org/packages/elm-explorations/test/latest/Test-Html-Event#event-builders))
813
-
814
- -}
815
- simulateDomEvent : (Query.Single msg -> Query.Single msg) -> ( String, Json.Encode.Value ) -> ProgramTest model msg effect -> ProgramTest model msg effect
816
- simulateDomEvent findTarget ( eventName, eventValue ) =
817
- andThen (simulateHelper ("simulateDomEvent " ++ String.Extra.escape eventName) findTarget ( eventName, eventValue ))
818
-
819
-
820
- {-| Simulates clicking a button.
821
-
822
- This function will find and click a `<button>` HTML node containing the given `buttonText`.
823
-
824
- It will also try to find and click elements with the accessibility label `role="button"`.
825
-
826
- If the button is disabled the test will fail.
827
-
828
- -}
829
- clickButton : String -> ProgramTest model msg effect -> ProgramTest model msg effect
830
- clickButton buttonText =
831
- let
832
- functionDescription =
833
- "clickButton " ++ String.Extra.escape buttonText
834
-
835
- checks : List ( String, ComplexQuery (Query.Single msg) -> ComplexQuery msg )
836
- checks =
837
- [ ( "<button> with text"
838
- , findNotDisabled (Just "find button")
839
- [ "button" ]
840
- Nothing
841
- [ Selector.tag "button"
842
- , Selector.containing [ Selector.text buttonText ]
843
- ]
844
- >> ComplexQuery.simulate Test.Html.Event.click
845
- )
846
- , ( "<button> with <img> with alt text"
847
- , findNotDisabled (Just "find button")
848
- [ "button" ]
849
- Nothing
850
- [ Selector.tag "button"
851
- , Selector.containing
852
- [ Selector.tag "img"
853
- , Selector.attribute (Html.Attributes.alt buttonText)
854
- ]
855
- ]
856
- >> ComplexQuery.simulate Test.Html.Event.click
857
- )
858
- , ( "<button> with aria-label"
859
- , findNotDisabled (Just "find button")
860
- [ "button" ]
861
- Nothing
862
- [ Selector.tag "button"
863
- , Selector.attribute (Html.Attributes.attribute "aria-label" buttonText)
864
- ]
865
- >> ComplexQuery.simulate Test.Html.Event.click
866
- )
867
- , ( "any element with role=\"button\" and text"
868
- , findNotDisabled (Just "find button")
869
- [ "button" ]
870
- (Just
871
- [ Selector.all
872
- [ Selector.tag "button"
873
- , Selector.attribute (Html.Attributes.attribute "role" "button")
874
- ]
875
- ]
876
- )
877
- [ Selector.attribute (Html.Attributes.attribute "role" "button")
878
- , Selector.containing [ Selector.text buttonText ]
879
- ]
880
- >> ComplexQuery.simulate Test.Html.Event.click
881
- )
882
- , ( "any element with role=\"button\" and aria-label"
883
- , findNotDisabled (Just "find button")
884
- [ "button" ]
885
- (Just
886
- [ Selector.all
887
- [ Selector.tag "button"
888
- , Selector.attribute (Html.Attributes.attribute "role" "button")
889
- ]
890
- ]
891
- )
892
- [ Selector.attribute (Html.Attributes.attribute "role" "button")
893
- , Selector.attribute (Html.Attributes.attribute "aria-label" buttonText)
894
- ]
895
- >> ComplexQuery.simulate Test.Html.Event.click
896
- )
897
- , ( "<form> with submit <button> with text"
898
- , ComplexQuery.findButNot (Just "find form")
899
- [ "form" ]
900
- { good =
901
- [ Selector.tag "form"
902
- , Selector.containing
903
- [ Selector.tag "button"
904
- , Selector.containing [ Selector.text buttonText ]
905
- ]
906
- ]
907
- , bads =
908
- [ [ Selector.tag "form"
909
- , Selector.containing
910
- [ Selector.tag "button"
911
- , Selector.attribute (Html.Attributes.type_ "button")
912
- , Selector.containing [ Selector.text buttonText ]
913
- ]
914
- ]
915
- , [ Selector.tag "form"
916
- , Selector.containing
917
- [ Selector.tag "button"
918
- , Selector.attribute (Html.Attributes.disabled True)
919
- , Selector.containing [ Selector.text buttonText ]
920
- ]
921
- ]
922
- ]
923
- , onError =
924
- [ Selector.tag "form"
925
- , Selector.containing
926
- [ Selector.tag "button"
927
- , Selector.attribute (Html.Attributes.disabled False)
928
- , Selector.attribute (Html.Attributes.type_ "submit")
929
- , Selector.containing [ Selector.text buttonText ]
930
- ]
931
- ]
932
- }
933
- >> ComplexQuery.simulate Test.Html.Event.submit
934
- )
935
- , ( "<form> with submit <input> with value"
936
- , ComplexQuery.findButNot (Just "find form")
937
- [ "form" ]
938
- { good =
939
- [ Selector.tag "form"
940
- , Selector.containing
941
- [ Selector.tag "input"
942
- , Selector.attribute (Html.Attributes.type_ "submit")
943
- , Selector.attribute (Html.Attributes.value buttonText)
944
- ]
945
- ]
946
- , bads =
947
- [ [ Selector.tag "form"
948
- , Selector.containing
949
- [ Selector.tag "input"
950
- , Selector.attribute (Html.Attributes.type_ "submit")
951
- , Selector.attribute (Html.Attributes.disabled True)
952
- , Selector.attribute (Html.Attributes.value buttonText)
953
- ]
954
- ]
955
- ]
956
- , onError =
957
- [ Selector.tag "form"
958
- , Selector.containing
959
- [ Selector.tag "input"
960
- , Selector.attribute (Html.Attributes.type_ "submit")
961
- , Selector.attribute (Html.Attributes.disabled False)
962
- , Selector.attribute (Html.Attributes.value buttonText)
963
- ]
964
- ]
965
- }
966
- >> ComplexQuery.simulate Test.Html.Event.submit
967
- )
968
- ]
969
- in
970
- simulateComplexQuery functionDescription
971
- (ComplexQuery.exactlyOneOf "Expected one of the following to exist" checks)
972
-
973
-
974
- findNotDisabled : Maybe String -> List String -> Maybe (List Selector) -> List Selector -> ComplexQuery (Query.Single msg) -> ComplexQuery (Query.Single msg)
975
- findNotDisabled description highlight additionalBad selectors =
976
- -- This is tricky because Test.Html doesn't provide a way to search for an attribute being *not* present.
977
- -- So we have to check if "disabled=True" *is* present, and manually force a failure if it is.
978
- -- (We can't just search for "disabled=False" because we need to allow elements that don't specify "disabled" at all.)
979
- ComplexQuery.findButNot description
980
- highlight
981
- { good = selectors
982
- , bads =
983
- List.filterMap identity
984
- [ Just (Selector.disabled True :: selectors)
985
- , additionalBad
986
- ]
987
- , onError = selectors ++ [ Selector.disabled False ]
988
- }
989
-
990
-
991
- {-| Simulates clicking a `<a href="...">` link.
992
-
993
- The parameters are:
994
-
995
- 1. The text of the `<a>` tag (which is the link text visible to the user).
996
-
997
- 2. The `href` of the `<a>` tag.
998
-
999
- NOTE: After [eeue56/elm-html-test#52](https://github.com/eeue56/elm-html-test/issues/52) is resolved,
1000
- a future release of this package will remove the `href` parameter.
1001
-
1002
- Note for testing single-page apps:
1003
- if the target `<a>` tag has an `onClick` handler,
1004
- then the message produced by the handler will be processed
1005
- and the `href` will not be followed.
1006
- NOTE: Currently this function cannot verify that the onClick handler
1007
- sets `preventDefault`, but this will be done in the future after
1008
- <https://github.com/eeue56/elm-html-test/issues/63> is resolved.
1009
-
1010
- -}
1011
- clickLink : String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
1012
- clickLink linkText href =
1013
- let
1014
- functionDescription =
1015
- "clickLink " ++ String.Extra.escape linkText
1016
-
1017
- findLink =
1018
- ComplexQuery.exactlyOneOf "Expected one of the following to exist"
1019
- [ ( "<a> with text"
1020
- , ComplexQuery.find (Just "find link")
1021
- [ "a" ]
1022
- [ Selector.tag "a"
1023
- , Selector.attribute (Html.Attributes.href href)
1024
- , Selector.containing [ Selector.text linkText ]
1025
- ]
1026
- )
1027
- , ( "<a> with aria-label"
1028
- , ComplexQuery.find (Just "find link")
1029
- [ "a" ]
1030
- [ Selector.tag "a"
1031
- , Selector.attribute (Html.Attributes.href href)
1032
- , Selector.attribute (Html.Attributes.attribute "aria-label" linkText)
1033
- ]
1034
- )
1035
- , ( "<a> with <img> with alt text"
1036
- , ComplexQuery.find (Just "find link")
1037
- [ "a" ]
1038
- [ Selector.tag "a"
1039
- , Selector.attribute (Html.Attributes.href href)
1040
- , Selector.containing
1041
- [ Selector.tag "img"
1042
- , Selector.attribute (Html.Attributes.alt linkText)
1043
- ]
1044
- ]
1045
- )
1046
- ]
1047
-
1048
- normalClick =
1049
- ( "click"
1050
- , Json.Encode.object
1051
- [ ( "ctrlKey", Json.Encode.bool False )
1052
- , ( "metaKey", Json.Encode.bool False )
1053
- ]
1054
- )
1055
-
1056
- ctrlClick =
1057
- ( "click"
1058
- , Json.Encode.object
1059
- [ ( "ctrlKey", Json.Encode.bool True )
1060
- , ( "metaKey", Json.Encode.bool False )
1061
- ]
1062
- )
1063
-
1064
- metaClick =
1065
- ( "click"
1066
- , Json.Encode.object
1067
- [ ( "ctrlKey", Json.Encode.bool False )
1068
- , ( "metaKey", Json.Encode.bool True )
1069
- ]
1070
- )
1071
-
1072
- respondsTo event single =
1073
- single
1074
- |> Test.Html.Event.simulate event
1075
- |> Test.Html.Event.toResult
1076
- |> Result.Extra.isOk
1077
-
1078
- tryClicking :
1079
- { otherwise :
1080
- Program model msg effect (SimulatedSub msg)
1081
- -> TestState model msg effect
1082
- -> Result Failure (TestState model msg effect)
1083
- }
1084
- -> Query.Single msg
1085
- -> Program model msg effect (SimulatedSub msg)
1086
- -> TestState model msg effect
1087
- -> Result Failure (TestState model msg effect)
1088
- tryClicking { otherwise } single program state =
1089
- if respondsTo normalClick single then
1090
- -- there is a click handler
1091
- -- first make sure the handler properly respects "Open in new tab", etc
1092
- if respondsTo ctrlClick single || respondsTo metaClick single then
1093
- Err
1094
- (CustomFailure functionDescription
1095
- (String.concat
1096
- [ "Found an `<a href=\"...\">` tag has an onClick handler, "
1097
- , "but the handler is overriding ctrl-click and meta-click.\n\n"
1098
- , "A properly behaved single-page app should not override ctrl- and meta-clicks on `<a>` tags "
1099
- , "because this prevents users from opening links in new tabs/windows.\n\n"
1100
- , "Use `onClickPreventDefaultForLinkWithHref` defined at <https://gist.github.com/avh4/712d43d649b7624fab59285a70610707> instead of `onClick` to fix this problem.\n\n"
1101
- , "See discussion of this issue at <https://github.com/elm-lang/navigation/issues/13>."
1102
- ]
1103
- )
1104
- )
1105
-
1106
- else
1107
- -- everything looks good, so simulate that event and ignore the `href`
1108
- single
1109
- |> Test.Html.Event.simulate normalClick
1110
- |> Test.Html.Event.toResult
1111
- |> Result.mapError (SimulateFailed functionDescription)
1112
- |> Result.andThen (\msg -> TestState.update msg program state)
1113
-
1114
- else
1115
- -- the link doesn't have a click handler
1116
- otherwise program state
1117
- in
1118
- runComplexQuery functionDescription findLink (tryClicking { otherwise = TestState.urlRequestHelper functionDescription href })
1119
-
1120
-
1121
- {-| Simulates replacing the text in an input field labeled with the given label.
1122
-
1123
- 1. The id of the input field
1124
- (which must match both the `id` attribute of the target `input` element,
1125
- and the `for` attribute of the `label` element),
1126
- or `""` if the `<input>` is a descendant of the `<label>`.
1127
-
1128
- NOTE: After [eeue56/elm-html-test#52](https://github.com/eeue56/elm-html-test/issues/52) is resolved,
1129
- a future release of this package will remove this parameter.
1130
-
1131
- 2. The label text of the input field.
1132
-
1133
- 3. The text that will be entered into the input field.
1134
-
1135
- There are a few different ways to accessibly label your input fields so that `fillIn` will find them:
1136
-
1137
- - You can place the `<input>` element inside a `<label>` element that also contains the label text.
1138
-
1139
- ```html
1140
- <label>
1141
- Favorite fruit
1142
- <input>
1143
- </label>
1144
- ```
1145
-
1146
- - You can place the `<input>` and a `<label>` element anywhere on the page and link them with a unique id.
1147
-
1148
- ```html
1149
- <label for="fruit">Favorite fruit</label>
1150
- <input id="fruit"></input>
1151
- ```
1152
-
1153
- - You can use the `aria-label` attribute.
1154
-
1155
- ```html
1156
- <input aria-label="Favorite fruit"></input>
1157
- ```
1158
-
1159
- If you need to target a `<textarea>` that does not have a label,
1160
- see [`fillInTextarea`](#fillInTextArea).
1161
-
1162
- If you need more control over finding the target element or creating the simulated event,
1163
- see [`simulateDomEvent`](#simulateDomEvent).
1164
-
1165
- -}
1166
- fillIn : String -> String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
1167
- fillIn fieldId label newContent programTest =
1168
- simulateLabeledInputHelper ("fillIn " ++ String.Extra.escape label)
1169
- fieldId
1170
- label
1171
- True
1172
- [-- TODO: should ensure that known special input types are not set, like `type="checkbox"`, etc?
1173
- ]
1174
- (Test.Html.Event.input newContent)
1175
- programTest
1176
-
1177
-
1178
- {-| Simulates replacing the text in a `<textarea>`.
1179
-
1180
- This function expects that there is only one `<textarea>` in the view.
1181
- If your view has more than one `<textarea>`,
1182
- prefer adding associated `<label>` elements and use [`fillIn`](#fillIn).
1183
- If you cannot add `<label>` elements see [`within`](#within).
1184
-
1185
- If you need more control over finding the target element or creating the simulated event,
1186
- see [`simulateDomEvent`](#simulateDomEvent).
1187
-
1188
- -}
1189
- fillInTextarea : String -> ProgramTest model msg effect -> ProgramTest model msg effect
1190
- fillInTextarea newContent =
1191
- simulateComplexQuery "fillInTextarea" <|
1192
- (ComplexQuery.find Nothing [ "textarea" ] [ Selector.tag "textarea" ]
1193
- >> ComplexQuery.simulate (Test.Html.Event.input newContent)
1194
- )
1195
-
1196
-
1197
- {-| Simulates setting the value of a checkbox labeled with the given label.
1198
-
1199
- The parameters are:
1200
-
1201
- 1. The id of the input field
1202
- (which must match both the `id` attribute of the target `input` element,
1203
- and the `for` attribute of the `label` element),
1204
- or `""` if the `<input>` is a descendant of the `<label>`.
1205
-
1206
- NOTE: After [eeue56/elm-html-test#52](https://github.com/eeue56/elm-html-test/issues/52) is resolved,
1207
- a future release of this package will remove this parameter.
1208
-
1209
- 2. The label text of the input field
1210
-
1211
- 3. A `Bool` indicating whether to check (`True`) or uncheck (`False`) the checkbox.
1212
-
1213
- NOTE: In the future, this will be generalized to work with
1214
- aria accessibility attributes in addition to working with standard HTML label elements.
1215
-
1216
- If you need more control over finding the target element or creating the simulated event,
1217
- see [`simulateDomEvent`](#simulateDomEvent).
1218
-
1219
- -}
1220
- check : String -> String -> Bool -> ProgramTest model msg effect -> ProgramTest model msg effect
1221
- check fieldId label willBecomeChecked programTest =
1222
- simulateLabeledInputHelper ("check " ++ String.Extra.escape label)
1223
- fieldId
1224
- label
1225
- False
1226
- [ Selector.attribute (Html.Attributes.type_ "checkbox") ]
1227
- (Test.Html.Event.check willBecomeChecked)
1228
- programTest
1229
-
1230
-
1231
- {-| Simulates choosing an option with the given text in a select with a given label
1232
-
1233
- The parameters are:
1234
-
1235
- 1. The id of the `<select>`
1236
- (which must match both the `id` attribute of the target `select` element,
1237
- and the `for` attribute of the `label` element),
1238
- or `""` if the `<select>` is a descendant of the `<label>`.
1239
-
1240
- NOTE: After [eeue56/elm-html-test#52](https://github.com/eeue56/elm-html-test/issues/52) is resolved,
1241
- a future release of this package will remove this parameter.
1242
-
1243
- 2. The label text of the select.
1244
-
1245
- 3. The `value` of the `<option>` that will be chosen.
1246
-
1247
- NOTE: After [eeue56/elm-html-test#51](https://github.com/eeue56/elm-html-test/issues/51) is resolved,
1248
- a future release of this package will remove this parameter.
1249
-
1250
- 4. The user-visible text of the `<option>` that will be chosen.
1251
-
1252
- Example: If you have a view like the following,
1253
-
1254
- import Html
1255
- import Html.Attributes exposing (for, id, value)
1256
- import Html.Events exposing (on, targetValue)
1257
-
1258
- Html.div []
1259
- [ Html.label [ for "pet-select" ] [ Html.text "Choose a pet" ]
1260
- , Html.select
1261
- [ id "pet-select", on "change" targetValue ]
1262
- [ Html.option [ value "dog" ] [ Html.text "Dog" ]
1263
- , Html.option [ value "hamster" ] [ Html.text "Hamster" ]
1264
- ]
1265
- ]
1266
-
1267
- you can simulate selecting an option like this:
1268
-
1269
- ProgramTest.selectOption "pet-select" "Choose a pet" "dog" "Dog"
1270
-
1271
- If you need more control over finding the target element or creating the simulated event,
1272
- see [`simulateDomEvent`](#simulateDomEvent).
1273
-
1274
- -}
1275
- selectOption : String -> String -> String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
1276
- selectOption fieldId label optionValue optionText =
1277
- let
1278
- functionDescription =
1279
- String.join " "
1280
- [ "selectOption"
1281
- , String.Extra.escape fieldId
1282
- , String.Extra.escape label
1283
- , String.Extra.escape optionValue
1284
- , String.Extra.escape optionText
1285
- ]
1286
- in
1287
- simulateComplexQuery functionDescription <|
1288
- ComplexQuery.check
1289
- "check label exists"
1290
- (ComplexQuery.find Nothing
1291
- [ "label" ]
1292
- [ Selector.tag "label"
1293
- , Selector.attribute (Html.Attributes.for fieldId)
1294
- , Selector.text label
1295
- ]
1296
- )
1297
- >> (ComplexQuery.find (Just "find select")
1298
- [ "select" ]
1299
- [ Selector.tag "select"
1300
- , Selector.id fieldId
1301
- ]
1302
- >> ComplexQuery.check
1303
- "check option exists"
1304
- (ComplexQuery.find Nothing
1305
- [ "option" ]
1306
- [ Selector.tag "option"
1307
- , Selector.attribute (Html.Attributes.value optionValue)
1308
- , Selector.text optionText
1309
- ]
1310
- )
1311
- )
1312
- >> ComplexQuery.simulate
1313
- ( "change"
1314
- , Json.Encode.object
1315
- [ ( "target"
1316
- , Json.Encode.object
1317
- [ ( "value", Json.Encode.string optionValue )
1318
- ]
1319
- )
1320
- ]
1321
- )
1322
-
1323
-
1324
- {-| Focus on a part of the view for a particular operation.
1325
-
1326
- For example, if your view produces the following HTML:
1327
-
1328
- ```html
1329
- <div>
1330
- <div id="sidebar">
1331
- <button>Submit</button>
1332
- </div>
1333
- <div id="content">
1334
- <button>Submit</button>
1335
- </div>
1336
- </div>
1337
- ```
1338
-
1339
- then the following will allow you to simulate clicking the "Submit" button in the sidebar
1340
- (simply using `clickButton "Submit"` would fail because there are two buttons matching that text):
1341
-
1342
- import Test.Html.Query as Query
1343
- import Test.Html.Selector exposing (id)
1344
-
1345
- programTest
1346
- |> ProgramTest.within
1347
- (Query.find [ id "sidebar" ])
1348
- (ProgramTest.clickButton "Submit")
1349
- |> ...
1350
-
1351
- -}
1352
- within : (Query.Single msg -> Query.Single msg) -> (ProgramTest model msg effect -> ProgramTest model msg effect) -> (ProgramTest model msg effect -> ProgramTest model msg effect)
1353
- within findTarget onScopedTest =
1354
- andThen (expectViewHelper "within" (findTarget >> Query.has []))
1355
- >> (andThen <|
1356
- \program state ->
1357
- case
1358
- Created
1359
- { state = Ok state
1360
- , program =
1361
- { program
1362
- | withinFocus = program.withinFocus >> findTarget
1363
- }
1364
- }
1365
- |> onScopedTest
1366
- of
1367
- Created created ->
1368
- case created.state of
1369
- Ok s ->
1370
- Ok s
1371
-
1372
- Err e ->
1373
- Err e.reason
1374
-
1375
- FailedToCreate failure ->
1376
- Err failure
1377
- )
1378
-
1379
-
1380
- {-| Asserts that an HTTP request to the specific url and method has been made.
1381
-
1382
- The parameters are:
1383
-
1384
- 1. The HTTP method of the expected request (typically `"GET"` or `"POST"`)
1385
- 2. The absolute URL of the expected request
1386
-
1387
- For example:
1388
-
1389
- ...
1390
- |> expectHttpRequestWasMade "GET" "https://example.com/api/data"
1391
-
1392
- If you want to check the headers or request body, see [`expectHttpRequest`](#expectHttpRequest).
1393
- If you expect multiple requests to have been made to the same endpoint, see [`expectHttpRequests`](#expectHttpRequests).
1394
-
1395
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1396
-
1397
- If you want to interact with the program more after this assertion, see [`ensureHttpRequestWasMade`](#ensureHttpRequestWasMade).
1398
-
1399
- -}
1400
- expectHttpRequestWasMade : String -> String -> ProgramTest model msg effect -> Expectation
1401
- expectHttpRequestWasMade method url programTest =
1402
- programTest
1403
- |> andThen (\_ -> expectHttpRequestHelper "expectHttpRequestWasMade" method url (checkSingleHttpRequest (always Expect.pass)))
1404
- |> done
1405
-
1406
-
1407
- {-| See the documentation for [`expectHttpRequestWasMade`](#expectHttpRequestWasMade).
1408
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
1409
- so that you can interact with the program further after this assertion.
1410
-
1411
- You should prefer `expectHttpRequestWasMade` when possible,
1412
- as having a single assertion per test can make the intent of your tests more clear.
1413
-
1414
- -}
1415
- ensureHttpRequestWasMade : String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
1416
- ensureHttpRequestWasMade method url =
1417
- andThen (\_ -> expectHttpRequestHelper "ensureHttpRequestWasMade" method url (checkSingleHttpRequest (always Expect.pass)))
1418
-
1419
-
1420
- {-| Allows you to check the details of a pending HTTP request.
1421
-
1422
- See the [“Expectations” section of `Test.Http`](Test-Http#expectations) for functions that might be helpful
1423
- in create an expectation on the request.
1424
-
1425
- If you only care about whether the a request was made to the correct URL, see [`expectHttpRequestWasMade`](#expectHttpRequestWasMade).
1426
-
1427
- ...
1428
- |> expectHttpRequest "POST"
1429
- "https://example.com/save"
1430
- (.body >> Expect.equal """{"content":"updated!"}""")
1431
-
1432
- If you expect multiple requests to have been made to the same endpoint, see [`expectHttpRequests`](#expectHttpRequests).
1433
-
1434
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1435
-
1436
- If you want to interact with the program more after this assertion, see [`ensureHttpRequest`](#ensureHttpRequest).
1437
-
1438
- -}
1439
- expectHttpRequest :
1440
- String
1441
- -> String
1442
- -> (Test.Http.HttpRequest msg msg -> Expectation)
1443
- -> ProgramTest model msg effect
1444
- -> Expectation
1445
- expectHttpRequest method url checkRequest =
1446
- andThen (\_ -> expectHttpRequestHelper "expectHttpRequest" method url (checkSingleHttpRequest checkRequest))
1447
- >> done
1448
-
1449
-
1450
- {-| See the documentation for [`expectHttpRequest`](#expectHttpRequest).
1451
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
1452
- so that you can interact with the program further after this assertion.
1453
-
1454
- You should prefer `expectHttpRequest` when possible,
1455
- as having a single assertion per test can make the intent of your tests more clear.
1456
-
1457
- -}
1458
- ensureHttpRequest :
1459
- String
1460
- -> String
1461
- -> (Test.Http.HttpRequest msg msg -> Expectation)
1462
- -> ProgramTest model msg effect
1463
- -> ProgramTest model msg effect
1464
- ensureHttpRequest method url checkRequest =
1465
- andThen (\_ -> expectHttpRequestHelper "ensureHttpRequest" method url (checkSingleHttpRequest checkRequest))
1466
-
1467
-
1468
- {-| Allows you to check the details of pending HTTP requests.
1469
-
1470
- See the [“Expectations” section of `Test.Http`](Test-Http#expectations) for functions that might be helpful
1471
- in create an expectation on the request.
1472
-
1473
- If your program will only have a single pending request to any particular URL, you can use the simpler [`expectHttpRequest`](#expectHttpRequest) (singular) or [`expectHttpRequestWasMade`](#expectHttpRequestWasMade) instead.
1474
-
1475
- ...
1476
- |> expectHttpRequests "POST"
1477
- "https://example.com/save"
1478
- (List.map .body >> Expect.equal ["""body1""", """body2"""])
1479
-
1480
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1481
-
1482
- If you want to interact with the program more after this assertion, see [`ensureHttpRequests`](#ensureHttpRequests).
1483
-
1484
- -}
1485
- expectHttpRequests :
1486
- String
1487
- -> String
1488
- -> (List (Test.Http.HttpRequest msg msg) -> Expectation)
1489
- -> ProgramTest model msg effect
1490
- -> Expectation
1491
- expectHttpRequests method url checkRequests =
1492
- andThen (\_ -> expectHttpRequestHelper "expectHttpRequests" method url (checkMultipleHttpRequests checkRequests))
1493
- >> done
1494
-
1495
-
1496
- {-| See the documentation for [`expectHttpRequests`](#expectHttpRequests).
1497
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
1498
- so that you can interact with the program further after this assertion.
1499
-
1500
- You should prefer `expectHttpRequests` when possible,
1501
- as having a single assertion per test can make the intent of your tests more clear.
1502
-
1503
- -}
1504
- ensureHttpRequests :
1505
- String
1506
- -> String
1507
- -> (List (Test.Http.HttpRequest msg msg) -> Expectation)
1508
- -> ProgramTest model msg effect
1509
- -> ProgramTest model msg effect
1510
- ensureHttpRequests method url checkRequests =
1511
- andThen (\_ -> expectHttpRequestHelper "ensureHttpRequests" method url (checkMultipleHttpRequests checkRequests))
1512
-
1513
-
1514
- checkSingleHttpRequest :
1515
- (Test.Http.HttpRequest msg msg -> Expectation)
1516
- -> List (Test.Http.HttpRequest msg msg)
1517
- -> Result (String -> { method : String, url : String } -> List ( String, String ) -> Failure) ()
1518
- checkSingleHttpRequest checkRequest requests =
1519
- case requests of
1520
- [] ->
1521
- Err (NoMatchingHttpRequest 1 0)
1522
-
1523
- [ request ] ->
1524
- case Test.Runner.getFailureReason (checkRequest request) of
1525
- Nothing ->
1526
- -- check succeeded
1527
- Ok ()
1528
-
1529
- Just reason ->
1530
- Err (\functionName _ _ -> ExpectFailed functionName reason.description reason.reason)
1531
-
1532
- (_ :: _ :: _) as many ->
1533
- Err (MultipleMatchingHttpRequest 1 (List.length many))
1534
-
1535
-
1536
- checkMultipleHttpRequests :
1537
- (List (Test.Http.HttpRequest msg msg) -> Expectation)
1538
- -> List (Test.Http.HttpRequest msg msg)
1539
- -> Result (String -> { method : String, url : String } -> List ( String, String ) -> Failure) ()
1540
- checkMultipleHttpRequests checkRequests requests =
1541
- case Test.Runner.getFailureReason (checkRequests requests) of
1542
- Nothing ->
1543
- -- check succeeded
1544
- Ok ()
1545
-
1546
- Just reason ->
1547
- Err (\functionName _ _ -> ExpectFailed functionName reason.description reason.reason)
1548
-
1549
-
1550
- expectHttpRequestHelper :
1551
- String
1552
- -> String
1553
- -> String
1554
- -> (List (Test.Http.HttpRequest msg msg) -> Result (String -> { method : String, url : String } -> List ( String, String ) -> Failure) ())
1555
- -> TestState model msg effect
1556
- -> Result Failure (TestState model msg effect)
1557
- expectHttpRequestHelper functionName method url checkRequests state =
1558
- case state.effectSimulation of
1559
- Nothing ->
1560
- Err (EffectSimulationNotConfigured functionName)
1561
-
1562
- Just simulation ->
1563
- checkRequests (MultiDict.get ( method, url ) simulation.state.http)
1564
- |> Result.map (\() -> state)
1565
- |> Result.mapError (\f -> f functionName { method = method, url = url } (MultiDict.keys simulation.state.http))
1566
-
1567
-
1568
- {-| Simulates an HTTP 200 response to a pending request with the given method and url.
1569
-
1570
- The parameters are:
1571
-
1572
- 1. The HTTP method of the request to simulate a response for (typically `"GET"` or `"POST"`)
1573
- 2. The URL of the request to simulate a response for
1574
- 3. The response body for the simulated response
1575
-
1576
- For example:
1577
-
1578
- ...
1579
- |> simulateHttpOk "GET"
1580
- "https://example.com/time.json"
1581
- """{"currentTime":1559013158}"""
1582
- |> ...
1583
-
1584
- If you need to simulate an error, a response with a different status code,
1585
- or a response with response headers,
1586
- see [`simulateHttpResponse`](#simulateHttpResponse).
1587
-
1588
- If you want to check the request headers or request body, use [`ensureHttpRequest`](#ensureHttpRequest)
1589
- immediately before using `simulateHttpOk`.
1590
-
1591
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1592
-
1593
- -}
1594
- simulateHttpOk : String -> String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
1595
- simulateHttpOk method url responseBody =
1596
- simulateHttpResponseHelper "simulateHttpOk"
1597
- method
1598
- url
1599
- 1
1600
- True
1601
- (Test.Http.httpResponse
1602
- { statusCode = 200
1603
- , body = responseBody
1604
- , headers = []
1605
- }
1606
- )
1607
-
1608
-
1609
- {-| Simulates a response to a pending HTTP request.
1610
- The test will fail if there is no pending request matching the given method and url.
1611
-
1612
- The parameters are:
1613
-
1614
- 1. The HTTP method of the request to simulate a response for (typically `"GET"` or `"POST"`)
1615
- 2. The URL of the request to simulate a response for
1616
- 3. The [`Http.Response`](https://package.elm-lang.org/packages/elm/http/latest/Http#Response) value for the simulated response. You may find it helpful to see the [“Responses” section in `Test.Http`](Test-Http#responses)
1617
- for convenient ways to create `Http.Response` values.
1618
-
1619
- For example:
1620
-
1621
- ...
1622
- |> simulateHttpResponse "GET"
1623
- "https://example.com/time.json"
1624
- Test.Http.networkError
1625
- |> ...
1626
- |> simulateHttpResponse "POST"
1627
- "https://example.com/api/v1/process_data"
1628
- (Test.Http.httpResponse
1629
- { statusCode : 204
1630
- , headers : [ ( "X-Procesing-Time", "1506ms") ]
1631
- , body : ""
1632
- }
1633
- )
1634
-
1635
- If you are simulating a 200 OK response and don't need to provide response headers,
1636
- you can use the simpler [`simulateHttpOk`](#simulateHttpOk).
1637
-
1638
- If you want to check the request headers or request body, use [`ensureHttpRequest`](#ensureHttpRequest)
1639
- immediately before using `simulateHttpResponse`.
1640
-
1641
- If your program will make multiple pending requests to the same URL, see [`simulateHttpResponseAdvanced`](#simulateHttpResponseAdvanced).
1642
-
1643
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1644
-
1645
- -}
1646
- simulateHttpResponse : String -> String -> Http.Response String -> ProgramTest model msg effect -> ProgramTest model msg effect
1647
- simulateHttpResponse method url response =
1648
- simulateHttpResponseHelper "simulateHttpResponse" method url 1 True response
1649
-
1650
-
1651
- {-| Simulates a response to one of several pending HTTP requests made to a given endpoint.
1652
-
1653
- This is the same as [`simulateHttpResponse`](#simulateHttpResponse),
1654
- except that the additional `Int` parameter specificies which request to resolve if multiple requests to the same method/URL are pending.
1655
-
1656
- -}
1657
- simulateHttpResponseAdvanced : String -> String -> Int -> Http.Response String -> ProgramTest model msg effect -> ProgramTest model msg effect
1658
- simulateHttpResponseAdvanced method url pendingRequestIndex response =
1659
- simulateHttpResponseHelper "simulateHttpResponseAdvanced" method url pendingRequestIndex False response
1660
-
1661
-
1662
- simulateHttpResponseHelper : String -> String -> String -> Int -> Bool -> Http.Response String -> ProgramTest model msg effect -> ProgramTest model msg effect
1663
- simulateHttpResponseHelper functionName method url pendingRequestIndex failIfMorePendingRequests response =
1664
- andThen <|
1665
- \program state ->
1666
- case state.effectSimulation of
1667
- Nothing ->
1668
- Err (EffectSimulationNotConfigured functionName)
1669
-
1670
- Just simulation ->
1671
- case
1672
- MultiDict.get ( method, url ) simulation.state.http
1673
- |> List.Extra.splitAt (pendingRequestIndex - 1)
1674
- of
1675
- ( prev, [] ) ->
1676
- Err (NoMatchingHttpRequest pendingRequestIndex (List.length prev) functionName { method = method, url = url } (MultiDict.keys simulation.state.http))
1677
-
1678
- ( prev, actualRequest :: rest ) ->
1679
- if failIfMorePendingRequests && rest /= [] then
1680
- Err (MultipleMatchingHttpRequest pendingRequestIndex (List.length prev + 1 + List.length rest) functionName { method = method, url = url } (MultiDict.keys simulation.state.http))
1681
-
1682
- else
1683
- let
1684
- resolveHttpRequest sim =
1685
- let
1686
- st =
1687
- sim.state
1688
- in
1689
- { sim | state = { st | http = MultiDict.set ( method, url ) (prev ++ rest) st.http } }
1690
- in
1691
- state
1692
- |> TestState.withSimulation
1693
- (resolveHttpRequest
1694
- >> EffectSimulation.queueTask (actualRequest.onRequestComplete response)
1695
- )
1696
- |> TestState.drain program
1697
-
1698
-
1699
- {-| Simulates the passing of time.
1700
- The `Int` parameter is the number of milliseconds to simulate.
1701
- This will cause any pending `Task.sleep`s to trigger if their delay has elapsed.
1702
-
1703
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1704
-
1705
- -}
1706
- advanceTime : Int -> ProgramTest model msg effect -> ProgramTest model msg effect
1707
- advanceTime delta =
1708
- andThen (TestState.advanceTime "advanceTime" delta)
1709
-
1710
-
1711
- {-| Lets you assert on the values that the program being tested has sent to an outgoing port.
1712
-
1713
- The parameters are:
1714
-
1715
- 1. The name of the port
1716
- 2. A JSON decoder corresponding to the type of the port
1717
- 3. A function that will receive the list of values sent to the port
1718
- since the start of the test (or since the last use of `ensureOutgoingPortValues`)
1719
- and return an `Expectation`
1720
-
1721
- For example:
1722
-
1723
- ...
1724
- |> expectOutgoingPortValues
1725
- "saveApiTokenToLocalStorage"
1726
- Json.Decode.string
1727
- (Expect.equal [ "975774a26612", "920facb1bac0" ])
1728
-
1729
- For a more detailed explanation and example, see the [“Testing programs with ports” guidebook](https://elm-program-test.netlify.com/ports.html).
1730
-
1731
- NOTE: You must use [`withSimulatedEffects`](#withSimulatedEffects) before you call [`start`](#start) to be able to use this function.
1732
-
1733
- If you want to interact with the program more after this assertion, see [`ensureOutgoingPortValues`](#ensureOutgoingPortValues).
1734
-
1735
- -}
1736
- expectOutgoingPortValues : String -> Json.Decode.Decoder a -> (List a -> Expectation) -> ProgramTest model msg effect -> Expectation
1737
- expectOutgoingPortValues portName decoder checkValues programTest =
1738
- programTest
1739
- |> expectOutgoingPortValuesHelper "expectOutgoingPortValues" portName decoder checkValues
1740
- |> done
1741
-
1742
-
1743
- {-| See the documentation for [`expectOutgoingPortValues`](#expectOutgoingPortValues).
1744
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
1745
- so that you can interact with the program further after this assertion.
1746
-
1747
- You should prefer `expectOutgoingPortValues` when possible,
1748
- as having a single assertion per test can make the intent of your tests more clear.
1749
-
1750
- -}
1751
- ensureOutgoingPortValues : String -> Json.Decode.Decoder a -> (List a -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
1752
- ensureOutgoingPortValues portName decoder checkValues programTest =
1753
- expectOutgoingPortValuesHelper "ensureOutgoingPortValues" portName decoder checkValues programTest
1754
-
1755
-
1756
- expectOutgoingPortValuesHelper : String -> String -> Json.Decode.Decoder a -> (List a -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
1757
- expectOutgoingPortValuesHelper functionName portName decoder checkValues =
1758
- andThen <|
1759
- \_ state ->
1760
- case state.effectSimulation of
1761
- Nothing ->
1762
- Err (EffectSimulationNotConfigured functionName)
1763
-
1764
- Just simulation ->
1765
- case allOk <| List.map (Json.Decode.decodeValue decoder) <| EffectSimulation.outgoingPortValues portName simulation of
1766
- Err errs ->
1767
- Err (CustomFailure (functionName ++ ": failed to decode port values") (List.map Json.Decode.errorToString errs |> String.join "\n"))
1768
-
1769
- Ok values ->
1770
- case Test.Runner.getFailureReason (checkValues values) of
1771
- Nothing ->
1772
- -- the check passed
1773
- Ok
1774
- { state
1775
- | effectSimulation =
1776
- Just (EffectSimulation.clearOutgoingPortValues portName simulation)
1777
- }
1778
-
1779
- Just reason ->
1780
- Err
1781
- (ExpectFailed (functionName ++ ": values sent to port \"" ++ portName ++ "\" did not match")
1782
- reason.description
1783
- reason.reason
1784
- )
1785
-
1786
-
1787
- allOk : List (Result x a) -> Result (List x) (List a)
1788
- allOk results =
1789
- let
1790
- step next acc =
1791
- case ( next, acc ) of
1792
- ( Ok n, Ok a ) ->
1793
- Ok (n :: a)
1794
-
1795
- ( Ok _, Err x ) ->
1796
- Err x
1797
-
1798
- ( Err n, Ok _ ) ->
1799
- Err [ n ]
1800
-
1801
- ( Err n, Err x ) ->
1802
- Err (n :: x)
1803
- in
1804
- List.foldl step (Ok []) results
1805
- |> Result.map List.reverse
1806
- |> Result.mapError List.reverse
1807
-
1808
-
1809
- {-| Lets you simulate a value being sent to the program being tested via an incoming port.
1810
-
1811
- The parameters are:
1812
-
1813
- 1. The name of the port
1814
- 2. The JSON representation of the incoming value
1815
-
1816
- For example, here we are simulating the program receiving a list of strings on the incoming port
1817
- `port resultsFromJavascript : (List String -> msg) -> Sub msg`:
1818
-
1819
- ...
1820
- |> ProgramTest.simulateIncomingPort
1821
- "resultsFromJavascript"
1822
- (Json.Encode.list Json.Encode.string
1823
- [ "Garden-path sentences can confuse the reader." ]
1824
- )
1825
-
1826
- For a more detailed explanation and example, see the [“Testing programs with ports” guidebook](https://elm-program-test.netlify.com/ports.html).
1827
-
1828
- NOTE: You must use [`withSimulatedSubscriptions`](#withSimulatedSubscriptions) before you call [`start`](#start) to be able to use this function.
1829
-
1830
- -}
1831
- simulateIncomingPort : String -> Json.Encode.Value -> ProgramTest model msg effect -> ProgramTest model msg effect
1832
- simulateIncomingPort portName value =
1833
- let
1834
- functionName =
1835
- "simulateIncomingPort \"" ++ portName ++ "\""
1836
- in
1837
- andThen <|
1838
- \program state ->
1839
- case program.subscriptions of
1840
- Nothing ->
1841
- Err (CustomFailure functionName "you MUST use ProgramTest.withSimulatedSubscriptions to be able to use simulateIncomingPort")
1842
-
1843
- Just fn ->
1844
- let
1845
- matches =
1846
- matchesFromSub (fn state.currentModel)
1847
-
1848
- matchesFromSub : SimulatedSub msg -> List (Result String msg)
1849
- matchesFromSub sub =
1850
- case sub of
1851
- SimulatedEffect.NoneSub ->
1852
- []
1853
-
1854
- SimulatedEffect.BatchSub subs_ ->
1855
- List.concatMap matchesFromSub subs_
1856
-
1857
- SimulatedEffect.PortSub pname decoder ->
1858
- if pname == portName then
1859
- Json.Decode.decodeValue decoder value
1860
- |> Result.mapError Json.Decode.errorToString
1861
- |> List.singleton
1862
-
1863
- else
1864
- []
1865
-
1866
- step : Result String msg -> TestState model msg effect -> Result Failure (TestState model msg effect)
1867
- step r tc =
1868
- case r of
1869
- Err message ->
1870
- Err
1871
- (CustomFailure functionName
1872
- ("the value provided does not match the type that the port is expecting:\n"
1873
- ++ message
1874
- )
1875
- )
1876
-
1877
- Ok msg ->
1878
- TestState.update msg program tc
1879
- in
1880
- if matches == [] then
1881
- Err (CustomFailure functionName "the program is not currently subscribed to the port")
1882
-
1883
- else
1884
- List.foldl (\match -> Result.andThen (step match)) (Ok state) matches
1885
-
1886
-
1887
- {-| Simulates a route change event (which would happen when your program is
1888
- a `Browser.application` and the user manually changes the URL in the browser's URL bar).
1889
-
1890
- The parameter may be an absolute URL or relative URL.
1891
-
1892
- -}
1893
- routeChange : String -> ProgramTest model msg effect -> ProgramTest model msg effect
1894
- routeChange url =
1895
- andThen (TestState.urlChangeHelper "routeChange" 0 url)
1896
-
1897
-
1898
- {-| Make an assertion about the current state of a `ProgramTest`'s model.
1899
-
1900
- When possible, you should prefer making assertions about the rendered view (see [`expectView`](#expectView))
1901
- or external requests made by your program (see [`expectHttpRequest`](#expectHttpRequest), [`expectOutgoingPortValues`](#expectOutgoingPortValues)),
1902
- as testing at the level that users and external services interact with your program
1903
- will make your tests more resilient to changes in the private implementation of your program.
1904
-
1905
- -}
1906
- expectModel : (model -> Expectation) -> ProgramTest model msg effect -> Expectation
1907
- expectModel assertion =
1908
- (andThen <|
1909
- \_ state ->
1910
- case assertion state.currentModel |> Test.Runner.getFailureReason of
1911
- Nothing ->
1912
- Ok state
1913
-
1914
- Just reason ->
1915
- Err (ExpectFailed "expectModel" reason.description reason.reason)
1916
- )
1917
- >> done
1918
-
1919
-
1920
- {-| Simulate the outcome of the last effect produced by the program being tested
1921
- by providing a function that can convert the last effect into `msg`s.
1922
-
1923
- The function you provide will be called with the effect that was returned by the most recent call to `update` or `init` in the `ProgramTest`.
1924
-
1925
- - If it returns `Err`, then the `ProgramTest` will enter a failure state with the provided error message.
1926
- - If it returns `Ok`, then the list of `msg`s will be applied in order via `ProgramTest.update`.
1927
-
1928
- NOTE: If you are simulating HTTP responses,
1929
- you should prefer more specific functions designed for that purpose.
1930
- You can find links to the relevant documentation in the [documentation index](#documentation-index).
1931
-
1932
- -}
1933
- simulateLastEffect : (effect -> Result String (List msg)) -> ProgramTest model msg effect -> ProgramTest model msg effect
1934
- simulateLastEffect toMsgs =
1935
- andThen <|
1936
- \program state ->
1937
- case toMsgs state.lastEffect of
1938
- Ok msgs ->
1939
- List.foldl (\msg -> Result.andThen (TestState.update msg program)) (Ok state) msgs
1940
-
1941
- Err message ->
1942
- Err (SimulateLastEffectFailed message)
1943
-
1944
-
1945
- expectLastEffectHelper : String -> (effect -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
1946
- expectLastEffectHelper functionName assertion =
1947
- andThen <|
1948
- \_ state ->
1949
- case assertion state.lastEffect |> Test.Runner.getFailureReason of
1950
- Nothing ->
1951
- Ok state
1952
-
1953
- Just reason ->
1954
- Err (ExpectFailed functionName reason.description reason.reason)
1955
-
1956
-
1957
- {-| See the documentation for [`expectLastEffect`](#expectLastEffect).
1958
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
1959
- so that you can interact with the program further after this assertion.
1960
-
1961
- You should prefer `expectLastEffect` when possible,
1962
- as having a single assertion per test can make the intent of your tests more clear.
1963
-
1964
- -}
1965
- ensureLastEffect : (effect -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
1966
- ensureLastEffect assertion programTest =
1967
- expectLastEffectHelper "ensureLastEffect" assertion programTest
1968
-
1969
-
1970
- {-| Makes an assertion about the last effect produced by a `ProgramTest`'s program.
1971
-
1972
- NOTE: If you are asserting about HTTP requests or outgoing ports,
1973
- you should prefer more specific functions designed for that purpose.
1974
- You can find links to the relevant documentation in the [documentation index](#documentation-index).
1975
-
1976
- If you want to interact with the program more after this assertion, see [`ensureLastEffect`](#ensureLastEffect).
1977
-
1978
- -}
1979
- expectLastEffect : (effect -> Expectation) -> ProgramTest model msg effect -> Expectation
1980
- expectLastEffect assertion programTest =
1981
- programTest
1982
- |> expectLastEffectHelper "expectLastEffect" assertion
1983
- |> done
1984
-
1985
-
1986
- expectViewHelper :
1987
- String
1988
- -> (Query.Single msg -> Expectation)
1989
- -> Program model msg effect sub
1990
- -> TestState model msg effect
1991
- -> Result Failure (TestState model msg effect)
1992
- expectViewHelper functionName assertion program state =
1993
- case
1994
- Program.renderView program state.currentModel
1995
- |> assertion
1996
- |> Test.Runner.getFailureReason
1997
- of
1998
- Nothing ->
1999
- Ok state
2000
-
2001
- Just reason ->
2002
- Err (ExpectFailed functionName reason.description reason.reason)
2003
-
2004
-
2005
- {-| See the documentation for [`expectView`](#expectView).
2006
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
2007
- so that you can interact with the program further after this assertion.
2008
-
2009
- You should prefer `expectView` when possible,
2010
- as having a single assertion per test can make the intent of your tests more clear.
2011
-
2012
- -}
2013
- ensureView : (Query.Single msg -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
2014
- ensureView assertion =
2015
- andThen (expectViewHelper "ensureView" assertion)
2016
-
2017
-
2018
- {-| See the documentation for [`expectViewHas`](#expectViewHas).
2019
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
2020
- so that you can interact with the program further after this assertion.
2021
-
2022
- You should prefer `expectViewHas` when possible,
2023
- as having a single assertion per test can make the intent of your tests more clear.
2024
-
2025
- -}
2026
- ensureViewHas : List Selector.Selector -> ProgramTest model msg effect -> ProgramTest model msg effect
2027
- ensureViewHas selector =
2028
- andThen (expectViewHelper "ensureViewHas" (Query.has selector))
2029
-
2030
-
2031
- {-| See the documentation for [`expectViewHasNot`](#expectViewHasNot).
2032
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
2033
- so that you can interact with the program further after this assertion.
2034
-
2035
- You should prefer `expectViewHasNot` when possible,
2036
- as having a single assertion per test can make the intent of your tests more clear.
2037
-
2038
- -}
2039
- ensureViewHasNot : List Selector.Selector -> ProgramTest model msg effect -> ProgramTest model msg effect
2040
- ensureViewHasNot selector =
2041
- andThen (expectViewHelper "ensureViewHasNot" (Query.hasNot selector))
2042
-
2043
-
2044
- {-| Makes an assertion about the current state of a `ProgramTest`'s view.
2045
-
2046
- If you want to interact with the program more after this assertion, see [`ensureView`](#ensureView).
2047
-
2048
- -}
2049
- expectView : (Query.Single msg -> Expectation) -> ProgramTest model msg effect -> Expectation
2050
- expectView assertion =
2051
- andThen (expectViewHelper "expectView" assertion)
2052
- >> done
2053
-
2054
-
2055
- {-| A simpler way to assert that a `ProgramTest`'s view matches a given selector.
2056
-
2057
- `expectViewHas [...selector...]` is the same as `expectView (Test.Html.Query.has [...selector...])`.
2058
-
2059
- If you want to interact with the program more after this assertion, see [`ensureViewHas`](#ensureViewHas).
2060
-
2061
- -}
2062
- expectViewHas : List Selector.Selector -> ProgramTest model msg effect -> Expectation
2063
- expectViewHas selector =
2064
- andThen (expectViewHelper "expectViewHas" (Query.has selector))
2065
- >> done
2066
-
2067
-
2068
- {-| A simpler way to assert that a `ProgramTest`'s view does not match a given selector.
2069
-
2070
- `expectViewHasNot [...selector...]` is the same as `expectView (Test.Html.Query.hasNot [...selector...])`.
2071
-
2072
- If you want to interact with the program more after this assertion, see [`ensureViewHasNot`](#ensureViewHasNot).
2073
-
2074
- -}
2075
- expectViewHasNot : List Selector.Selector -> ProgramTest model msg effect -> Expectation
2076
- expectViewHasNot selector =
2077
- andThen (expectViewHelper "expectViewHasNot" (Query.hasNot selector))
2078
- >> done
2079
-
2080
-
2081
- {-| Ends a `ProgramTest`, reporting any errors that occurred.
2082
-
2083
- You can also end a `ProgramTest` using any of the functions starting with `expect*`.
2084
- In fact, you should prefer using one of the `expect*` functions when possible,
2085
- as doing so will [make the intent of your test more clear](https://www.artima.com/weblogs/viewpost.jsp?thread=35578).
2086
-
2087
- -}
2088
- done : ProgramTest model msg effect -> Expectation
2089
- done programTest =
2090
- case toFailure programTest of
2091
- Nothing ->
2092
- Expect.pass
2093
-
2094
- Just failure ->
2095
- Expect.fail (Failure.toString failure)
2096
-
2097
-
2098
- {-| Asserts that the program ended by navigating away to another URL.
2099
-
2100
- The parameter is:
2101
-
2102
- 1. The expected URL that the program should have navigated away to.
2103
-
2104
- If your program is an application that manages URL changes
2105
- (created with [`createApplication`](#createApplication)),
2106
- then you probably want [`expectBrowserUrl`](#expectBrowserUrl) instead.
2107
-
2108
- -}
2109
- expectPageChange : String -> ProgramTest model msg effect -> Expectation
2110
- expectPageChange expectedUrl programTest =
2111
- case toFailure programTest of
2112
- Just (ChangedPage cause finalLocation) ->
2113
- Url.toString finalLocation |> Expect.equal expectedUrl
2114
-
2115
- Just _ ->
2116
- programTest |> done
2117
-
2118
- Nothing ->
2119
- Expect.fail "expectPageChange: expected to have navigated to a different URL, but no links were clicked and no browser navigation was simulated"
2120
-
2121
-
2122
- {-| Asserts on the current value of the browser URL bar in the simulated test environment.
2123
-
2124
- The parameter is:
2125
-
2126
- 1. A function that asserts on the current URL. Typically you will use `Expect.equal` with the exact URL you expect.
2127
-
2128
- If your program is _not_ an application that manages URL changes
2129
- and you want to assert that the user clicked a link that goes to an external web page,
2130
- then you probably want [`expectPageChange`](#expectPageChange) instead.
2131
-
2132
- -}
2133
- expectBrowserUrl : (String -> Expectation) -> ProgramTest model msg effect -> Expectation
2134
- expectBrowserUrl checkUrl programTest =
2135
- expectBrowserUrlHelper "expectBrowserUrl" checkUrl programTest
2136
- |> done
2137
-
2138
-
2139
- {-| See the documentation for [`expectBrowserUrl`](#expectBrowserUrl).
2140
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
2141
- so that you can interact with the program further after this assertion.
2142
-
2143
- You should prefer `expectBrowserUrl` when possible,
2144
- as having a single assertion per test can make the intent of your tests more clear.
2145
-
2146
- -}
2147
- ensureBrowserUrl : (String -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
2148
- ensureBrowserUrl checkUrl programTest =
2149
- expectBrowserUrlHelper "ensureBrowserUrl" checkUrl programTest
2150
-
2151
-
2152
- expectBrowserUrlHelper : String -> (String -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
2153
- expectBrowserUrlHelper functionName checkUrl =
2154
- andThen <|
2155
- \_ state ->
2156
- case Maybe.map .currentLocation state.navigation of
2157
- Nothing ->
2158
- Err (ProgramDoesNotSupportNavigation functionName)
2159
-
2160
- Just url ->
2161
- case Test.Runner.getFailureReason (checkUrl (Url.toString url)) of
2162
- Nothing ->
2163
- -- check succeeded
2164
- Ok state
2165
-
2166
- Just reason ->
2167
- Err (ExpectFailed functionName reason.description reason.reason)
2168
-
2169
-
2170
- {-| Asserts on the current browser history in the simulated test environment.
2171
- This only makes sense if you are using [`withSimulatedEffects`](#withSimulatedEffects)
2172
- and the function you provide to it produces
2173
- [`SimulatedEffect.Navigation.replaceUrl`](SimulatedEffect-Navigation#replaceUrl) or
2174
- [`SimulatedEffect.Navigation.pushUrl`](SimulatedEffect-Navigation#pushUrl)
2175
- for one or more of your effects.
2176
- The previous URL is added to the simulated browser history whenever a `pushUrl` effect is simulated.
2177
-
2178
- The parameter is:
2179
-
2180
- 1. A function that asserts on the current browser history (most recent at the head) to an expectation.
2181
-
2182
- Example: If there's only one expected item in the history or if you want check the complete history since the start of the test, use this with `Expect.equal`
2183
-
2184
- createApplication { ... }
2185
- |> withBaseUrl "https://example.com/resource/123"
2186
- |> start ()
2187
- |> clickButton "Details"
2188
- |> expectBrowserHistory (Expect.equal [ "https://example.com/resource/123/details" ])
2189
-
2190
- Example: If there might be multiple items in the history and you only want to check the most recent item:
2191
-
2192
- createApplication { ... }
2193
- |> withBaseUrl "https://example.com/resource/123"
2194
- |> start ()
2195
- |> clickButton "Details"
2196
- |> clickButton "Calendar"
2197
- |> expectBrowserHistory (List.head >> Expect.equal (Just "https://example.com/resource/123/calendar"))
2198
-
2199
- If you need to assert on the current URL, see [`expectBrowserUrl`](#expectBrowserUrl).
2200
-
2201
- -}
2202
- expectBrowserHistory : (List String -> Expectation) -> ProgramTest model msg effect -> Expectation
2203
- expectBrowserHistory checkHistory programTest =
2204
- expectBrowserHistoryHelper "expectBrowserHistory" checkHistory programTest
2205
- |> done
2206
-
2207
-
2208
- {-| See the documentation for [`expectBrowserHistory`](#expectBrowserHistory).
2209
- This is the same except that it returns a `ProgramTest` instead of an `Expectation`
2210
- so that you can interact with the program further after this assertion.
2211
-
2212
- You should prefer `expectBrowserHistory` when possible,
2213
- as having a single assertion per test can make the intent of your tests more clear.
2214
-
2215
- -}
2216
- ensureBrowserHistory : (List String -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
2217
- ensureBrowserHistory checkHistory programTest =
2218
- expectBrowserHistoryHelper "ensureBrowserHistory" checkHistory programTest
2219
-
2220
-
2221
- expectBrowserHistoryHelper : String -> (List String -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect
2222
- expectBrowserHistoryHelper functionName checkHistory =
2223
- andThen <|
2224
- \_ state ->
2225
- case Maybe.map .browserHistory state.navigation of
2226
- Nothing ->
2227
- -- TODO: use withBaseUrl error
2228
- Err (ProgramDoesNotSupportNavigation functionName)
2229
-
2230
- Just browserHistoryYes ->
2231
- case Test.Runner.getFailureReason (checkHistory (List.map Url.toString browserHistoryYes)) of
2232
- Nothing ->
2233
- -- check succeeded
2234
- Ok state
2235
-
2236
- Just reason ->
2237
- Err (ExpectFailed functionName reason.description reason.reason)
2238
-
2239
-
2240
- {-| `fail` can be used to report custom errors if you are writing your own convenience functions to deal with program tests.
2241
-
2242
- For example, this function checks for a particular structure in the program's view,
2243
- but will also fail the ProgramTest if the `expectedCount` parameter is invalid:
2244
-
2245
- expectNotificationCount : Int -> ProgramTest model msg effect -> Expectation
2246
- expectNotificationCount expectedCount programTest =
2247
- if expectedCount <= 0 then
2248
- programTest
2249
- |> ProgramTest.fail "expectNotificationCount"
2250
- ("expectedCount must be positive, but was: " ++ String.fromInt expectedCount)
2251
-
2252
- else
2253
- programTest
2254
- |> expectViewHas
2255
- [ Test.Html.Selector.class "notifications"
2256
- , Test.Html.Selector.text (toString expectedCount)
2257
- ]
2258
-
2259
- If you are writing a convenience function that is creating a program test, see [`createFailed`](#createFailed).
2260
-
2261
- -}
2262
- fail : String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
2263
- fail assertionName failureMessage =
2264
- andThen <| \_ _ -> Err (CustomFailure assertionName failureMessage)
2265
-
2266
-
2267
- {-| `createFailed` can be used to report custom errors if you are writing your own convenience functions to _create_ program tests.
2268
-
2269
- NOTE: if you are writing a convenience function that takes a `ProgramTest` as input, you should use [`fail`](#fail) instead,
2270
- as it provides more context in the test failure message.
2271
-
2272
- The parameters are:
2273
-
2274
- 1. The name of your helper function (displayed in failure messages)
2275
- 2. The failure message (also included in the failure message)
2276
-
2277
- For example:
2278
-
2279
- -- JsonSchema and MyProgram are imaginary modules for this example
2280
-
2281
-
2282
- import JsonSchema exposing (Schema, validateJsonSchema)
2283
- import MyProgram exposing (Model, Msg)
2284
- import ProgramTest exposing (ProgramTest)
2285
-
2286
- createWithValidatedJson : Schema -> String -> ProgramTest Model Msg (Cmd Msg)
2287
- createWithValidatedJson schema json =
2288
- case validateJsonSchema schema json of
2289
- Err message ->
2290
- ProgramTest.createFailed
2291
- "createWithValidatedJson"
2292
- ("JSON schema validation failed:\n" ++ message)
2293
-
2294
- Ok () ->
2295
- ProgramTest.createElement
2296
- { init = MyProgram.init
2297
- , update = MyProgram.update
2298
- , view = MyProgram.view
2299
- }
2300
- |> ProgramTest.start json
2301
-
2302
- -}
2303
- createFailed : String -> String -> ProgramTest model msg effect
2304
- createFailed functionName failureMessage =
2305
- FailedToCreate (CustomFailure functionName failureMessage)
2306
-
2307
-
2308
- {-| This can be used for advanced helper functions where you want to continue a test but need the data
2309
- sent out through ports later in your test.
2310
-
2311
- NOTE: If you do not need this advanced functionality,
2312
- prefer [`expectOutgoingPortValues`](#expectOutgoingPortValues) instead.
2313
-
2314
- -}
2315
- getOutgoingPortValues : String -> ProgramTest model msg effect -> Result (ProgramTest model msg effect) (List Json.Encode.Value)
2316
- getOutgoingPortValues portName programTest =
2317
- case programTest of
2318
- FailedToCreate _ ->
2319
- Err programTest
2320
-
2321
- Created ({ program, state } as created) ->
2322
- case state of
2323
- Err _ ->
2324
- Err programTest
2325
-
2326
- Ok s ->
2327
- case s.effectSimulation of
2328
- Nothing ->
2329
- Err <|
2330
- Created
2331
- { created
2332
- | state =
2333
- TestResult.fail (EffectSimulationNotConfigured "getPortValues") s
2334
- }
2335
-
2336
- Just effects ->
2337
- Dict.get portName effects.outgoingPortValues
2338
- |> Maybe.withDefault []
2339
- |> Ok