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.
- package/README.md +1 -1
- package/codegen/elm-pages-codegen.js +66 -118
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +20 -20
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +5 -5
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +21 -21
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/src/RouteBuilder.elm +23 -23
- package/generator/src/SharedTemplate.elm +2 -2
- package/generator/src/SiteConfig.elm +2 -2
- package/generator/src/cli.js +2 -2
- package/generator/src/compatibility-key.js +1 -1
- package/generator/src/error-formatter.js +7 -3
- package/generator/src/render.js +6 -15
- package/generator/src/request-cache.js +34 -4
- package/generator/static-code/hmr.js +16 -2
- package/package.json +1 -1
- package/src/ApiRoute.elm +13 -16
- package/src/BackendTask/Env.elm +11 -8
- package/src/BackendTask/File.elm +49 -10
- package/src/BackendTask/Glob.elm +6 -6
- package/src/BackendTask/Http.elm +49 -13
- package/src/BackendTask/Port.elm +59 -47
- package/src/BackendTask.elm +8 -22
- package/src/FatalError.elm +101 -0
- package/src/Form.elm +3 -2
- package/src/Internal/ApiRoute.elm +5 -5
- package/src/Pages/Generate.elm +300 -103
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Platform/Cli.elm +21 -41
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +24 -48
- package/src/Pages/Internal/Platform/StaticResponses.elm +18 -31
- package/src/Pages/Internal/Script.elm +2 -2
- package/src/Pages/Manifest.elm +2 -2
- package/src/Pages/ProgramConfig.elm +7 -7
- package/src/Pages/Script.elm +4 -4
- package/src/Pages/SiteConfig.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +1 -23
- package/src/Server/Request.elm +3 -2
- package/src/Exception.elm +0 -37
- package/src/MultiDict.elm +0 -49
- package/src/PairingHeap.elm +0 -137
- package/src/Parser/Extra/String.elm +0 -33
- package/src/Parser/Extra.elm +0 -69
- package/src/ProgramTest/ComplexQuery.elm +0 -360
- package/src/ProgramTest/EffectSimulation.elm +0 -122
- package/src/ProgramTest/Failure.elm +0 -367
- package/src/ProgramTest/HtmlHighlighter.elm +0 -116
- package/src/ProgramTest/HtmlParserHacks.elm +0 -58
- package/src/ProgramTest/HtmlRenderer.elm +0 -73
- package/src/ProgramTest/Program.elm +0 -30
- package/src/ProgramTest/StringLines.elm +0 -26
- package/src/ProgramTest/TestHtmlHacks.elm +0 -132
- package/src/ProgramTest/TestHtmlParser.elm +0 -201
- package/src/ProgramTest.elm +0 -2339
- package/src/Query/Extra.elm +0 -55
- package/src/SimulatedEffect/Cmd.elm +0 -69
- package/src/SimulatedEffect/Http.elm +0 -330
- package/src/SimulatedEffect/Navigation.elm +0 -69
- package/src/SimulatedEffect/Ports.elm +0 -62
- package/src/SimulatedEffect/Process.elm +0 -24
- package/src/SimulatedEffect/Sub.elm +0 -48
- package/src/SimulatedEffect/Task.elm +0 -252
- package/src/SimulatedEffect/Time.elm +0 -25
- package/src/SimulatedEffect.elm +0 -42
- package/src/String/Extra.elm +0 -6
- package/src/Test/Http.elm +0 -145
- package/src/TestResult.elm +0 -35
- package/src/TestState.elm +0 -305
- package/src/Url/Extra.elm +0 -100
- package/src/Vendored/Diff.elm +0 -321
- package/src/Vendored/Failure.elm +0 -217
- package/src/Vendored/FormatMonochrome.elm +0 -44
- package/src/Vendored/Highlightable.elm +0 -53
package/src/ProgramTest.elm
DELETED
|
@@ -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
|