@curtissimo/elm-hot 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG +40 -0
  2. package/CODE_OF_CONDUCT.md +74 -0
  3. package/CONTRIBUTING.md +51 -0
  4. package/LICENSE.txt +33 -0
  5. package/README.md +84 -0
  6. package/package.json +37 -0
  7. package/resources/hmr.js +519 -0
  8. package/src/index.js +6 -0
  9. package/src/inject.js +46 -0
  10. package/test/client.js +63 -0
  11. package/test/fixtures/BrowserApplicationCounter.elm +136 -0
  12. package/test/fixtures/BrowserApplicationCounter.html +22 -0
  13. package/test/fixtures/BrowserApplicationCounterDeepKey.elm +137 -0
  14. package/test/fixtures/BrowserApplicationCounterDeepKey.html +18 -0
  15. package/test/fixtures/BrowserApplicationCounterMultiKey.elm +158 -0
  16. package/test/fixtures/BrowserApplicationCounterMultiKey.html +22 -0
  17. package/test/fixtures/BrowserApplicationMissingNavKeyError.elm +62 -0
  18. package/test/fixtures/BrowserApplicationMissingNavKeyError.html +22 -0
  19. package/test/fixtures/BrowserApplicationNavKeyMoved.elm +159 -0
  20. package/test/fixtures/BrowserApplicationNavKeyMoved.html +22 -0
  21. package/test/fixtures/BrowserApplicationWithNull.elm +142 -0
  22. package/test/fixtures/BrowserApplicationWithNull.html +22 -0
  23. package/test/fixtures/BrowserDocumentCounter.elm +57 -0
  24. package/test/fixtures/BrowserDocumentCounter.html +24 -0
  25. package/test/fixtures/BrowserElementCounter.elm +55 -0
  26. package/test/fixtures/BrowserElementCounter.html +24 -0
  27. package/test/fixtures/BrowserSandboxCounter.elm +48 -0
  28. package/test/fixtures/BrowserSandboxCounter.html +21 -0
  29. package/test/fixtures/DebugBrowserApplication.elm +139 -0
  30. package/test/fixtures/DebugBrowserApplication.html +22 -0
  31. package/test/fixtures/DebugEmbed.elm +48 -0
  32. package/test/fixtures/DebugEmbed.html +21 -0
  33. package/test/fixtures/DebugFullscreen.elm +57 -0
  34. package/test/fixtures/DebugFullscreen.html +22 -0
  35. package/test/fixtures/FullScreenEmptyInit.elm +51 -0
  36. package/test/fixtures/FullScreenEmptyInit.html +19 -0
  37. package/test/fixtures/InitSideEffects.elm +65 -0
  38. package/test/fixtures/InitSideEffects.html +27 -0
  39. package/test/fixtures/MainWithTasks.elm +70 -0
  40. package/test/fixtures/MainWithTasks.html +21 -0
  41. package/test/fixtures/MultiMain.html +21 -0
  42. package/test/fixtures/MultiMain1.elm +48 -0
  43. package/test/fixtures/MultiMain2.elm +48 -0
  44. package/test/fixtures/PortsEmbed.elm +64 -0
  45. package/test/fixtures/PortsEmbed.html +27 -0
  46. package/test/fixtures/PortsFullscreen.elm +70 -0
  47. package/test/fixtures/PortsFullscreen.html +26 -0
  48. package/test/fixtures/build.sh +29 -0
  49. package/test/fixtures/elm.json +24 -0
  50. package/test/server.js +73 -0
  51. package/test/test.js +320 -0
  52. package/test.sh +16 -0
@@ -0,0 +1,64 @@
1
+ port module PortsEmbed exposing (main)
2
+
3
+ import Browser
4
+ import Html exposing (Html, button, div, h1, p, span, text)
5
+ import Html.Attributes exposing (id)
6
+ import Html.Events exposing (onClick)
7
+
8
+
9
+ main : Program () Model Msg
10
+ main =
11
+ Browser.element
12
+ { init = init
13
+ , view = view
14
+ , update = update
15
+ , subscriptions = subscriptions
16
+ }
17
+
18
+
19
+ port toJavaScript : Int -> Cmd msg
20
+
21
+
22
+ port fromJavaScript : (Int -> msg) -> Sub msg
23
+
24
+
25
+ type alias Model =
26
+ { count : Int }
27
+
28
+
29
+ init : () -> ( Model, Cmd Msg )
30
+ init _ =
31
+ ( { count = 0 }, Cmd.none )
32
+
33
+
34
+ type Msg
35
+ = Increment
36
+ | GotNewValue Int
37
+
38
+
39
+ update : Msg -> Model -> ( Model, Cmd msg )
40
+ update msg model =
41
+ case msg of
42
+ Increment ->
43
+ ( model, toJavaScript model.count )
44
+
45
+ GotNewValue _ ->
46
+ ( { model | count = model.count + 1 }, Cmd.none )
47
+
48
+
49
+ subscriptions : Model -> Sub Msg
50
+ subscriptions _ =
51
+ fromJavaScript GotNewValue
52
+
53
+
54
+ view : Model -> Html Msg
55
+ view model =
56
+ div []
57
+ [ h1 [] [ text "PortsEmbed" ]
58
+ , span [ id "code-version" ] [ text "code: v1" ]
59
+ , p []
60
+ [ text "Counter value is: "
61
+ , span [ id "counter-value" ] [ text (String.fromInt model.count) ]
62
+ ]
63
+ , button [ onClick Increment, id "counter-button" ] [ text "+" ]
64
+ ]
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE HTML>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>Main</title>
7
+ <script type="text/javascript" src="client.js"></script>
8
+ <script type="text/javascript" src="build/PortsEmbed.js"></script>
9
+ </head>
10
+
11
+ <body>
12
+ <div id="embed">to be filled out later</div>
13
+ <script>
14
+ connect("PortsEmbed");
15
+ var app = Elm.PortsEmbed.init({
16
+ node: document.getElementById('embed'),
17
+ flags: {}
18
+ });
19
+
20
+ app.ports.toJavaScript.subscribe(function (n) {
21
+ console.log("received " + n + " from Elm");
22
+ app.ports.fromJavaScript.send(n);
23
+ });
24
+ </script>
25
+ </body>
26
+
27
+ </html>
@@ -0,0 +1,70 @@
1
+ port module PortsFullscreen exposing (..)
2
+
3
+ import Browser
4
+ import Html exposing (button, h1, p, span, text)
5
+ import Html.Attributes exposing (id)
6
+ import Html.Events exposing (onClick)
7
+
8
+
9
+ main : Program Flags Model Msg
10
+ main =
11
+ Browser.document
12
+ { init = init
13
+ , view = view
14
+ , update = update
15
+ , subscriptions = subscriptions
16
+ }
17
+
18
+
19
+ port toJavaScript : Int -> Cmd msg
20
+
21
+
22
+ port fromJavaScript : (Int -> msg) -> Sub msg
23
+
24
+
25
+ type alias Flags =
26
+ { n : Int }
27
+
28
+
29
+ type alias Model =
30
+ { count : Int }
31
+
32
+
33
+ init : Flags -> ( Model, Cmd Msg )
34
+ init flags =
35
+ ( { count = flags.n }, Cmd.none )
36
+
37
+
38
+ type Msg
39
+ = Increment
40
+ | GotNewValue Int
41
+
42
+
43
+ update : Msg -> Model -> ( Model, Cmd msg )
44
+ update msg model =
45
+ case msg of
46
+ Increment ->
47
+ ( model, toJavaScript model.count )
48
+
49
+ GotNewValue _ ->
50
+ ( { model | count = model.count + 1 }, Cmd.none )
51
+
52
+
53
+ subscriptions : Model -> Sub Msg
54
+ subscriptions _ =
55
+ fromJavaScript GotNewValue
56
+
57
+
58
+ view : Model -> Browser.Document Msg
59
+ view model =
60
+ { title = "PortsFullscreen"
61
+ , body =
62
+ [ h1 [] [ text "PortsFullscreen" ]
63
+ , span [ id "code-version" ] [ text "code: v1" ]
64
+ , p []
65
+ [ text "Counter value is: "
66
+ , span [ id "counter-value" ] [ text (String.fromInt model.count) ]
67
+ ]
68
+ , button [ onClick Increment, id "counter-button" ] [ text "+" ]
69
+ ]
70
+ }
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE HTML>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>Main</title>
7
+ <script type="text/javascript" src="client.js"></script>
8
+ <script type="text/javascript" src="build/PortsFullscreen.js"></script>
9
+ </head>
10
+
11
+ <body>
12
+ <script>
13
+ connect("PortsFullscreen");
14
+ var app = Elm.PortsFullscreen.init({
15
+ flags: {
16
+ n: 0
17
+ }
18
+ });
19
+ app.ports.toJavaScript.subscribe(function (n) {
20
+ console.log("received " + n + " from Elm");
21
+ app.ports.fromJavaScript.send(n);
22
+ });
23
+ </script>
24
+ </body>
25
+
26
+ </html>
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+
3
+ cd "${0%/*}" # set CWD to the dir containing the script
4
+
5
+ set -o errexit
6
+ set -o pipefail
7
+ set -o nounset
8
+
9
+ for filename in *.elm; do
10
+
11
+ if [[ $filename == MultiMain* ]] ;
12
+ then
13
+ # these will be compiled later as a single unit
14
+ continue
15
+ fi
16
+
17
+ extraArgs=""
18
+
19
+ if [[ $filename == Debug* ]] ;
20
+ then
21
+ extraArgs="--debug"
22
+ fi
23
+
24
+ echo "Compiling $filename"
25
+ elm make $filename --output=build/"$(basename "$filename" .elm).js" ${extraArgs}
26
+ done
27
+
28
+ echo "Compiling MultiMain1.elm and MultiMain2.elm"
29
+ elm make MultiMain1.elm MultiMain2.elm --output=build/MultiMain.js
@@ -0,0 +1,24 @@
1
+ {
2
+ "type": "application",
3
+ "source-directories": [
4
+ "."
5
+ ],
6
+ "elm-version": "0.19.1",
7
+ "dependencies": {
8
+ "direct": {
9
+ "elm/browser": "1.0.2",
10
+ "elm/core": "1.0.2",
11
+ "elm/html": "1.0.0",
12
+ "elm/json": "1.1.3",
13
+ "elm/url": "1.0.0"
14
+ },
15
+ "indirect": {
16
+ "elm/time": "1.0.0",
17
+ "elm/virtual-dom": "1.0.2"
18
+ }
19
+ },
20
+ "test-dependencies": {
21
+ "direct": {},
22
+ "indirect": {}
23
+ }
24
+ }
package/test/server.js ADDED
@@ -0,0 +1,73 @@
1
+ const express = require("express");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+ const chokidar = require("chokidar");
5
+ const app = express();
6
+
7
+ app.setMaxListeners(30);
8
+
9
+ const { inject } = require("../src/inject.js");
10
+
11
+ const pathToTestFixtures = path.join(__dirname, "./fixtures");
12
+ const pathToBuildDir = path.join(pathToTestFixtures, "build");
13
+
14
+ try {
15
+ fs.mkdirSync(pathToBuildDir);
16
+ } catch (error) {
17
+ if (error.code !== "EEXIST") throw error;
18
+ }
19
+ const watcher = chokidar.watch(pathToBuildDir, { persistent: true });
20
+ watcher.setMaxListeners(30);
21
+
22
+ app.get("/client.js", (req, res) =>
23
+ res.sendFile(path.join(__dirname, "./client.js")),
24
+ );
25
+
26
+ app.get("/:filename.html", (req, res) => {
27
+ const filename = req.params.filename + ".html";
28
+ res.sendFile(path.join(pathToTestFixtures, filename));
29
+ });
30
+
31
+ app.get("/build/:filename.js", function (req, res) {
32
+ const filename = req.params.filename + ".js";
33
+ const pathToElmCodeJS = path.join(pathToBuildDir, filename);
34
+ const originalElmCodeJS = fs.readFileSync(pathToElmCodeJS, {
35
+ encoding: "utf8",
36
+ });
37
+ const fullyInjectedCode = inject(originalElmCodeJS);
38
+ res.send(fullyInjectedCode);
39
+ });
40
+
41
+ app.get("/stream-:programName", function (req, res) {
42
+ const programName = req.params.programName;
43
+ res.writeHead(200, {
44
+ Connection: "keep-alive",
45
+ "Content-Type": "text/event-stream",
46
+ });
47
+
48
+ watcher.on("change", function (pathThatChanged, stats) {
49
+ if (pathThatChanged.endsWith(programName + ".js")) {
50
+ //console.log("Pushing HMR event to client");
51
+ const relativeLoadPath = path.relative(
52
+ pathToTestFixtures,
53
+ pathThatChanged,
54
+ );
55
+ res.write(`data: ${relativeLoadPath}\n\n`);
56
+ }
57
+ });
58
+ });
59
+
60
+ function startServer(port) {
61
+ return app.listen(port);
62
+ }
63
+
64
+ if (require.main === module) {
65
+ startServer(3000);
66
+ console.log("Server listening at http://127.0.0.1:3000");
67
+ }
68
+
69
+ module.exports = {
70
+ app,
71
+ watcher,
72
+ startServer: startServer,
73
+ };
package/test/test.js ADDED
@@ -0,0 +1,320 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+
4
+ const test = require("ava");
5
+ const puppeteer = require("puppeteer");
6
+ const childProcess = require("child_process");
7
+
8
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
9
+
10
+ const { watcher, startServer } = require("./server.js");
11
+
12
+ test.before(async () => {
13
+ console.log("Building the Elm code");
14
+ const output = childProcess.execFileSync("./build.sh", {
15
+ cwd: "./test/fixtures",
16
+ });
17
+ console.log("Elm build.sh output: " + output);
18
+
19
+ global.browser = await puppeteer.launch({
20
+ headless: true, // default is true; set to false when debugging failed tests
21
+ slowMo: 50, // introduce a little delay between each operation
22
+ dumpio: false, // default is false; set to true when debugging failed tests
23
+ args: ["--no-sandbox"], // required for CI builds
24
+ protocolTimeout: 360000,
25
+ });
26
+ });
27
+
28
+ test.beforeEach(async (t) => {
29
+ t.context.httpServer = startServer();
30
+ t.context.serverUrl =
31
+ "http://127.0.0.1:" + t.context.httpServer.address().port;
32
+
33
+ const page = await browser.newPage();
34
+ page.on("pageerror", (error) => {
35
+ console.log("BROWSER: uncaught exception: " + error);
36
+ });
37
+ page.on("requestfailed", (request) => {
38
+ console.log("BROWSER: request failed: " + request.url());
39
+ });
40
+ page.on("response", (response) => {
41
+ if (!response.ok())
42
+ console.error(
43
+ "BROWSER: response: " + response.url() + " " + response.status(),
44
+ );
45
+ });
46
+ t.context.page = page;
47
+ });
48
+
49
+ test.afterEach(async (t) => {
50
+ await t.context.page.close();
51
+ if (t.context.httpServer !== undefined) {
52
+ const server = t.context.httpServer;
53
+ await new Promise((good, bad) => {
54
+ server.close(good);
55
+ });
56
+ }
57
+ });
58
+
59
+ test.after.always(async () => {
60
+ if (typeof browser !== "undefined") {
61
+ // normally browser will be defined, but it might not be if a `before` hook failed
62
+ await browser.close();
63
+ await watcher.close();
64
+ }
65
+ });
66
+
67
+ /*
68
+ AVA tests are run concurrently in separate processes. This is good because
69
+ each integration test is slow. But you must also be careful to isolate
70
+ the tests (especially files on disk)!
71
+ */
72
+
73
+ test("counter HMR preserves count (Browser.element)", async (t) => {
74
+ await doCounterTest(t, "BrowserElementCounter");
75
+ });
76
+
77
+ test("counter HMR preserves count (Browser.document)", async (t) => {
78
+ await doCounterTest(t, "BrowserDocumentCounter");
79
+ });
80
+
81
+ test("counter HMR preserves count (Browser.sandbox)", async (t) => {
82
+ await doCounterTest(t, "BrowserSandboxCounter");
83
+ });
84
+
85
+ test("counter HMR preserves count (Browser.application)", async (t) => {
86
+ await doBrowserApplicationTest(t, "BrowserApplicationCounter");
87
+ });
88
+
89
+ test("Browser.Navigation.Key can be found in a nested record", async (t) => {
90
+ await doBrowserApplicationTest(t, "BrowserApplicationCounterDeepKey");
91
+ });
92
+
93
+ test("Browser.Navigation.Key can be found in the variants of a union type", async (t) => {
94
+ await doBrowserApplicationTest(t, "BrowserApplicationCounterMultiKey");
95
+ });
96
+
97
+ test("Browser.Navigation.Key can be found even when the keypath changes", async (t) => {
98
+ // see https://github.com/klazuka/elm-hot/issues/35
99
+ await doBrowserApplicationTest(t, "BrowserApplicationNavKeyMoved");
100
+ });
101
+
102
+ test("init side effects do not run after HMR", async (t) => {
103
+ // see https://github.com/klazuka/elm-hot-webpack-loader/issues/1
104
+ await doCounterTest(t, "InitSideEffects");
105
+ });
106
+
107
+ test("fullscreen apps which pass empty args to init works", async (t) => {
108
+ // see https://github.com/klazuka/elm-hot/issues/11
109
+ await doCounterTest(t, "FullScreenEmptyInit");
110
+ });
111
+
112
+ test("multiple Elm Main modules", async (t) => {
113
+ const testName = "MultiMain";
114
+ const page = t.context.page;
115
+ await page.goto(`${t.context.serverUrl}/${testName}.html`);
116
+
117
+ const inc = "#incrementer ";
118
+ const dec = "#decrementer ";
119
+
120
+ // interleave various updates to the 2 separate Elm apps
121
+ // one app increments a counter; the other decrements
122
+ // each app has its own counter value.
123
+ await checkCodeVersion(t, page, "inc-v1", inc);
124
+ await checkCodeVersion(t, page, "dec-v1", dec);
125
+ await stepTheCounter(t, page, 1, inc);
126
+ await modifyElmCode(t, testName, page, "inc-v1", "inc-v2", inc);
127
+ await stepTheCounter(t, page, 2, inc);
128
+ await stepTheCounter(t, page, 3, inc);
129
+ await modifyElmCode(t, testName, page, "dec-v1", "dec-v2", dec);
130
+ await stepTheCounter(t, page, -1, dec);
131
+ await stepTheCounter(t, page, 4, inc);
132
+ await modifyElmCode(t, testName, page, "inc-v2", "inc-v3", inc);
133
+ await stepTheCounter(t, page, 5, inc);
134
+ await stepTheCounter(t, page, -2, dec);
135
+ await modifyElmCode(t, testName, page, "inc-v3", "inc-v4", inc);
136
+ await modifyElmCode(t, testName, page, "dec-v2", "dec-v3", dec);
137
+ await stepTheCounter(t, page, -3, dec);
138
+ await stepTheCounter(t, page, 6, inc);
139
+ });
140
+
141
+ test("counter HMR preserves count (embed app in DOM with debugger)", async (t) => {
142
+ await doCounterTest(t, "DebugEmbed");
143
+ });
144
+
145
+ test("counter HMR preserves count (fullscreen app in DOM with debugger)", async (t) => {
146
+ await doCounterTest(t, "DebugFullscreen");
147
+ });
148
+
149
+ test("counter HMR preserves count (Browser.application with debugger)", async (t) => {
150
+ await doBrowserApplicationTest(t, "DebugBrowserApplication");
151
+ });
152
+
153
+ test("pending async tasks are cancelled when HMR is performed", async (t) => {
154
+ const testName = "MainWithTasks";
155
+ const page = t.context.page;
156
+ await page.goto(`${t.context.serverUrl}/${testName}.html`);
157
+ const sleepyTaskMillis = 5000; // this MUST be in sync with the code in `MainWithTasks.elm`
158
+ const slop = 500; // additional millis to wait just to make sure that everything has completed
159
+
160
+ await checkCodeVersion(t, page, "v1");
161
+ t.is(await getCounterValue(page), 0);
162
+
163
+ // trigger sleepy increment but do HMR halfway through, cancelling the increment
164
+ await incrementCounter(page);
165
+ t.is(await getCounterValue(page), 0); // still 0 because the increment is async
166
+ await delay(sleepyTaskMillis / 2);
167
+ await modifyElmCode(t, testName, page, "v1", "v2");
168
+ await delay(sleepyTaskMillis / 2 + slop);
169
+ t.is(await getCounterValue(page), 0); // should still be 0 because the increment was cancelled
170
+
171
+ // trigger sleepy increment but this time allow it to complete
172
+ await incrementCounter(page);
173
+ await delay(sleepyTaskMillis + slop);
174
+ t.is(await getCounterValue(page), 1); // should now be 1 because the increment had time to finish
175
+ await modifyElmCode(t, testName, page, "v2", "v3");
176
+ });
177
+
178
+ test("ports are reconnected after HMR (embed case)", async (t) => {
179
+ await doCounterTest(t, "PortsEmbed");
180
+ });
181
+
182
+ test("ports are reconnected after HMR (fullscreen case)", async (t) => {
183
+ await doCounterTest(t, "PortsFullscreen");
184
+ });
185
+
186
+ test("an Elm model containing `null` should not crash", async (t) => {
187
+ // see https://github.com/klazuka/elm-hot/pull/36
188
+ await doCounterTest(t, "BrowserApplicationWithNull");
189
+ });
190
+
191
+ async function doCounterTest(t, testName) {
192
+ const page = t.context.page;
193
+ await page.goto(`${t.context.serverUrl}/${testName}.html`);
194
+
195
+ await checkCodeVersion(t, page, "v1");
196
+ await stepTheCounter(t, page, 1);
197
+ await modifyElmCode(t, testName, page, "v1", "v2");
198
+ await stepTheCounter(t, page, 2);
199
+ await modifyElmCode(t, testName, page, "v2", "v3");
200
+ await stepTheCounter(t, page, 3);
201
+ await stepTheCounter(t, page, 4);
202
+ await modifyElmCode(t, testName, page, "v3", "v4");
203
+ await stepTheCounter(t, page, 5);
204
+ await stepTheCounter(t, page, 6);
205
+ await stepTheCounter(t, page, 7);
206
+ await modifyElmCode(t, testName, page, "v4", "v5");
207
+ await stepTheCounter(t, page, 8);
208
+ }
209
+
210
+ async function doBrowserApplicationTest(t, testName) {
211
+ const page = t.context.page;
212
+ await page.goto(`${t.context.serverUrl}/${testName}.html`);
213
+
214
+ const inc = "#incrementer ";
215
+ const dec = "#decrementer ";
216
+
217
+ await checkCodeVersion(t, page, "v1");
218
+ await stepTheCounter(t, page, 1, inc);
219
+ await stepTheCounter(t, page, 2, inc);
220
+ await stepTheCounter(t, page, 3, inc);
221
+ await clickLink(page, "#nav-decrement");
222
+ await stepTheCounter(t, page, 2, dec);
223
+ await modifyElmCode(t, testName, page, "v1", "v2");
224
+ await stepTheCounter(t, page, 1, dec);
225
+ await clickLink(page, "#nav-increment");
226
+ await stepTheCounter(t, page, 2, inc);
227
+ await modifyElmCode(t, testName, page, "v2", "v3");
228
+ await stepTheCounter(t, page, 3, inc);
229
+ await stepTheCounter(t, page, 4, inc);
230
+ await modifyElmCode(t, testName, page, "v3", "v4");
231
+ await stepTheCounter(t, page, 5, inc);
232
+ await modifyElmCode(t, testName, page, "v4", "v5");
233
+ await clickLink(page, "#nav-decrement");
234
+ await stepTheCounter(t, page, 4, dec);
235
+ await stepTheCounter(t, page, 3, dec);
236
+ await clickLink(page, "#nav-increment");
237
+ await stepTheCounter(t, page, 4, inc);
238
+ }
239
+
240
+ test("if Browser.Navigation.Key cannot be found, degrade gracefully", async (t) => {
241
+ const testName = "BrowserApplicationMissingNavKeyError";
242
+ // There was a bug (https://github.com/klazuka/elm-hot/issues/15) which caused an infinite loop
243
+ // when the root of the model was a union type and the Browser.Navigation.Key could not be found.
244
+ const page = t.context.page;
245
+ await page.goto(`${t.context.serverUrl}/${testName}.html`);
246
+ // If we made it this far, then the page successfully loaded.
247
+ t.pass();
248
+ });
249
+
250
+ // TEST BUILDING BLOCKS
251
+
252
+ async function stepTheCounter(t, page, expectedPost, selectorScope = "") {
253
+ await incrementCounter(page, selectorScope);
254
+ t.is(await getCounterValue(page, selectorScope), expectedPost);
255
+ }
256
+
257
+ async function modifyElmCode(
258
+ t,
259
+ testName,
260
+ page,
261
+ oldVersion,
262
+ newVersion,
263
+ selectorScope = "",
264
+ ) {
265
+ const pathToElmCode = path.join(__dirname, `./fixtures/build/${testName}.js`);
266
+ const elmCode = fs.readFileSync(pathToElmCode, { encoding: "utf8" });
267
+ const originalFragment = `elm$html$Html$text('code: ${oldVersion}')`;
268
+ const modifiedFragment = `elm$html$Html$text('code: ${newVersion}')`;
269
+ const newElmCode = elmCode.replace(originalFragment, modifiedFragment);
270
+ if (newElmCode === elmCode) {
271
+ throw Error(
272
+ "Failed to modify the compiled Elm code on disk: pattern not found",
273
+ );
274
+ }
275
+ fs.writeFileSync(pathToElmCode, newElmCode);
276
+ // console.log("Finished writing to the compiled Elm file on disk");
277
+ await page.waitForSelector(selectorScope + codeVersionId);
278
+ // console.log("done sleeping");
279
+
280
+ await checkCodeVersion(t, page, newVersion, selectorScope);
281
+ }
282
+
283
+ async function clickLink(page, selector) {
284
+ // console.log("Clicking link at selector", selector);
285
+ await Promise.all([
286
+ page.waitForNavigation({ timeout: 5000 }),
287
+ page.click(selector, { delay: 10 }),
288
+ ]);
289
+ }
290
+
291
+ // ELM COUNTER MANIPULATION
292
+
293
+ // these must match the ids used in the Elm counter example program
294
+ const buttonId = "#counter-button";
295
+ const valueId = "#counter-value";
296
+ const codeVersionId = "#code-version";
297
+
298
+ async function incrementCounter(page, selectorScope = "") {
299
+ // console.log("Stepping the counter " + selectorScope);
300
+ await page.click(selectorScope + buttonId, { delay: 10 });
301
+ }
302
+
303
+ async function getCounterValue(page, selectorScope = "") {
304
+ const selector =
305
+ selectorScope.length > 0 ? `${selectorScope} ${valueId}` : valueId;
306
+ const counterValue = await page.locator(selector).waitHandle();
307
+ const textContent = await counterValue?.evaluate((el) => el.textContent);
308
+ return Number.parseInt(textContent, 10);
309
+ }
310
+
311
+ async function checkCodeVersion(t, page, expectedVersion, selectorScope = "") {
312
+ const selector =
313
+ selectorScope.length > 0
314
+ ? `${selectorScope} ${codeVersionId}`
315
+ : codeVersionId;
316
+ const codeVersion = await page.locator(selector).waitHandle();
317
+ const textContent = await codeVersion?.evaluate((el) => el.textContent);
318
+ t.is(textContent, `code: ${expectedVersion}`);
319
+ return textContent;
320
+ }
package/test.sh ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if ! command -v elm &>/dev/null; then
4
+ echo "Could not find the Elm compiler on your path"
5
+ exit 1
6
+ fi
7
+
8
+ ACTUAL_ELM_VERSION=$(elm --version)
9
+
10
+ if ! echo $ACTUAL_ELM_VERSION | grep -q "0.19.[01]"; then
11
+ echo "Expected Elm compiler version 0.19.0 or 0.19.1, got $ACTUAL_ELM_VERSION"
12
+ exit 2
13
+ fi
14
+
15
+ echo "Testing with Elm $ACTUAL_ELM_VERSION"
16
+ npx ava --timeout=6m