@jsenv/test 1.0.0 → 1.0.2

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 ADDED
@@ -0,0 +1,314 @@
1
+ # @jsenv/test [![npm package](https://img.shields.io/npm/v/@jsenv/test.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/test)
2
+
3
+ Executing test files in web browsers and/or Node.js.
4
+ This tool enforce test files to be written as **standard** files, without any sort of complexity.
5
+
6
+ # 1. Writing tests on web browsers
7
+
8
+ This section demonstrates how to write a test that will be executed in a web browser.
9
+
10
+ The function that will be tested is inside "add.js" file:
11
+
12
+ ```js
13
+ export const add = (a, b) => a + b
14
+ ```
15
+
16
+ The demonstration uses the following file structure:
17
+
18
+ <pre>
19
+ project/
20
+ src/
21
+ <strong>add.js</strong>
22
+ index.html
23
+ package.json
24
+ </pre>
25
+
26
+ At the end of the demo, the file structure will be like this:
27
+
28
+ <pre>
29
+ project/
30
+ scripts/
31
+ <strong>dev.mjs</strong>
32
+ <strong>test.mjs</strong>
33
+ src/
34
+ add.js
35
+ <strong>add.test.html</strong>
36
+ index.html
37
+ package.json
38
+ </pre>
39
+
40
+ ## 1.1 Writing the test file
41
+
42
+ *src/add.test.html*
43
+
44
+ ```html
45
+ <!DOCTYPE html>
46
+ <html>
47
+ <head>
48
+ <title>Title</title>
49
+ <meta charset="utf-8" />
50
+ <link rel="icon" href="data:," />
51
+ </head>
52
+
53
+ <body>
54
+ <script type="module">
55
+ import { add } from "./add.js"
56
+
57
+ const actual = add(1, 2)
58
+ const expected = 3
59
+ if (actual !== expected) {
60
+ throw new Error(`add(1,2) should return 3, got ${actual}`)
61
+ }
62
+ </script>
63
+ </body>
64
+ </html>
65
+ ```
66
+
67
+ ## 1.2 Executing the test file
68
+
69
+ *scripts/dev.mjs*: will start a web server that is needed to executed "add.test.html" in a browser.
70
+
71
+ ```js
72
+ import { startDevServer } from "@jsenv/core"
73
+
74
+ await startDevServer({
75
+ sourceDirectoryUrl: new URL("../src/", import.meta.url),
76
+ port: 3456,
77
+ })
78
+ ```
79
+
80
+ *scripts/test.mjs*: will start a web browser and use it to execute all test files.
81
+
82
+ ```js
83
+ import { executeTestPlan, chromium } from "@jsenv/test"
84
+
85
+ await executeTestPlan({
86
+ rootDirectoryUrl: new URL("../", import.meta.url),
87
+ testPlan: {
88
+ "./src/**/*.test.html": {
89
+ chromium: {
90
+ runtime: chromium()
91
+ },
92
+ },
93
+ },
94
+ webServer: {
95
+ origin: "http://localhost:3456",
96
+ rootDirectoryUrl: new URL("../src/", import.meta.url),
97
+ moduleUrl: new URL("./dev.mjs", import.meta.url),
98
+ },
99
+ })
100
+ ```
101
+
102
+ Command to install dependencies:
103
+
104
+ ```console
105
+ npm i --save-dev @jsenv/core
106
+ npm i --save-dev @jsenv/test
107
+ npm i --save-dev playwright
108
+ ```
109
+
110
+ ☝️ Playwright is used by `@jsenv/test` to start a web browser, see [playwright website](https://github.com/microsoft/playwright)<sup>↗</sup>.
111
+
112
+ Command to execute tests:
113
+
114
+ ```console
115
+ node ./scripts/test.mjs
116
+ ```
117
+
118
+ ### 1.3 Executing on more browsers
119
+
120
+ ```js
121
+ import {
122
+ executeTestPlan,
123
+ chromium,
124
+ firefox,
125
+ webkit,
126
+ } from "@jsenv/test"
127
+
128
+ await executeTestPlan({
129
+ rootDirectoryUrl: new URL("../", import.meta.url),
130
+ testPlan: {
131
+ "./src/**/*.test.html": {
132
+ chromium: {
133
+ runtime: chromium()
134
+ },
135
+ firefox: {
136
+ runtime: firefox()
137
+ },
138
+ webkit: {
139
+ runtime: webkit()
140
+ },
141
+ },
142
+ },
143
+ webServer: {
144
+ origin: "http://localhost:3456",
145
+ rootDirectoryUrl: new URL("../src/", import.meta.url),
146
+ moduleUrl: new URL("./dev.mjs", import.meta.url),
147
+ },
148
+ })
149
+ ```
150
+
151
+ ## 2. Writing tests on Node.js
152
+
153
+ This section demonstrates how to write a test that will be executed in Node.js.
154
+
155
+ The function that will be tested is inside "add.js" file:
156
+
157
+ ```js
158
+ export const add = (a, b) => a + b
159
+ ```
160
+
161
+ The demonstration uses the following file structure:
162
+
163
+ <pre>
164
+ project/
165
+ src/
166
+ <strong>add.js</strong>
167
+ package.json
168
+ </pre>
169
+
170
+ At the end of the demo, the file structure will be like this:
171
+
172
+ <pre>
173
+ project/
174
+ scripts/
175
+ <strong>test.mjs</strong>
176
+ src/
177
+ add.js
178
+ tests/
179
+ <strong>add.test.mjs</strong>
180
+ package.json
181
+ </pre>
182
+
183
+ ## 2.1 Writing the test file
184
+
185
+ *add.test.mjs*
186
+
187
+ ```js
188
+ import { add } from "./add.js"
189
+
190
+ const actual = add(1, 2)
191
+ const expected = 3
192
+ if (actual !== expected) {
193
+ throw new Error(`add(1,2) should return 3, got ${actual}`)
194
+ }
195
+ ```
196
+
197
+ ## 2.2 Executing the test file
198
+
199
+ *scripts/test.mjs*
200
+
201
+ ```js
202
+ import { executeTestPlan, nodeWorkerThread } from "@jsenv/test"
203
+
204
+ await executeTestPlan({
205
+ rootDirectoryUrl: new URL("../", import.meta.url),
206
+ testPlan: {
207
+ "./tests/**/*.test.mjs": {
208
+ node: {
209
+ runtime: nodeWorkerThread()
210
+ },
211
+ },
212
+ },
213
+ })
214
+ ```
215
+
216
+ Command to install dependencies:
217
+
218
+ ```console
219
+ npm i --save-dev @jsenv/test
220
+ ```
221
+
222
+ Command to execute tests:
223
+
224
+ ```console
225
+ node ./scripts/test.mjs
226
+ ```
227
+
228
+ ## 3. Assertion library
229
+
230
+ To have a basic example, the part of the code comparing `actual` and `expected` was done without an assertion library.
231
+ In pratice a test would likely use one. The diff below shows how the assertion can be written using [@jsenv/assert](../assert). Note that any other assertion library would work.
232
+
233
+ ```diff
234
+ + import { assert } from "@jsenv/assert"
235
+ import { add } from "./add.js"
236
+
237
+ const actual = add(1, 2)
238
+ const expected = 3
239
+ - if (actual !== expected) {
240
+ - throw new Error(`add(1,2) should return 3, got ${actual}`)
241
+ - }
242
+ + assert({ actual, expected })
243
+ ```
244
+
245
+ ## 4. API
246
+
247
+ ## 4.1 executeTestPlan
248
+
249
+ ```js
250
+ import { executeTestPlan } from "@jsenv/test"
251
+
252
+ const report = await executeTestPlan({
253
+ keepRunning: false, // true would keep process alive and all browsers opened even when tests are done
254
+ coverageEnabled: false, // collect code coverage while executing tests
255
+ })
256
+ report // contains many information about test executions
257
+ ```
258
+
259
+ ## 4.2 chromium/firefox/webkit
260
+
261
+ Params can be used to configure how the browser runtime is started
262
+
263
+ ```js
264
+ chromium({
265
+ headful: true // browser UI would be displayed while running tests
266
+ })
267
+ ```
268
+
269
+ ## 4.3 nodeWorkerThread/nodeChildProcess
270
+
271
+ Params can be used to configure how node child process or node worker thread is started.
272
+ Both runtime share the same arguments.
273
+
274
+ ```js
275
+ import {
276
+ nodeWorkerThread,
277
+ nodeChildProcess
278
+ } from "@jsenv/test"
279
+
280
+ nodeWorkerThread({
281
+ commandLineOptions: [], // see https://nodejs.org/api/cli.html#options
282
+ env: null // will be written on process.env, see https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback
283
+ importMap: null, // can be used to override import resolution (redirect to other files during test)
284
+ })
285
+ ```
286
+
287
+ ## 4.4 Allocated time
288
+
289
+ Each file is given 30s to execute.
290
+ If this duration is exceeded the browser tab (or node process/worker thread) is closed and execution is considered as failed.
291
+ This duration can be configured as shown below:
292
+
293
+ ```js
294
+ import {
295
+ executeTestPlan,
296
+ nodeWorkerThread
297
+ } from "@jsenv/test"
298
+
299
+ await executeTestPlan({
300
+ rootDirectoryUrl: new URL("../", import.meta.url),
301
+ testPlan: {
302
+ "./tests/**/*.test.mjs": {
303
+ node: {
304
+ runtime: nodeWorkerThread(),
305
+ allocatedMs: 60_000
306
+ },
307
+ },
308
+ },
309
+ })
310
+ ```
311
+
312
+ ☝️ Code above changes the default allocated time to 60s.
313
+
314
+
@@ -2081,6 +2081,8 @@ ansiEscapes.clearTerminal = isWindows ? `${ansiEscapes.eraseScreen}${ESC}0f`
2081
2081
  // 3. Moves cursor to the top-left position
2082
2082
  // More info: https://www.real-world-systems.com/docs/ANSIcode.html
2083
2083
  : `${ansiEscapes.eraseScreen}${ESC}3J${ESC}H`;
2084
+ ansiEscapes.enterAlternativeScreen = ESC + '?1049h';
2085
+ ansiEscapes.exitAlternativeScreen = ESC + '?1049l';
2084
2086
  ansiEscapes.beep = BEL;
2085
2087
  ansiEscapes.link = (text, url) => [OSC, '8', SEP, SEP, url, BEL, text, OSC, '8', SEP, SEP, BEL].join('');
2086
2088
  ansiEscapes.image = (buffer, options = {}) => {
@@ -4503,7 +4505,7 @@ const createRuntimeUsingPlaywright = ({
4503
4505
  stopOnExit: true,
4504
4506
  playwrightLaunchOptions: {
4505
4507
  ...playwrightLaunchOptions,
4506
- headless: !headful
4508
+ headless: headful === undefined ? !keepRunning : !headful
4507
4509
  }
4508
4510
  });
4509
4511
  if (browser._initializer.version) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/test",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  "playwright": "1.x"
34
34
  },
35
35
  "dependencies": {
36
- "@jsenv/core": "35.0.0",
36
+ "@jsenv/core": "35.0.1",
37
37
  "@jsenv/abort": "4.2.4",
38
38
  "@jsenv/ast": "3.0.6",
39
39
  "@jsenv/filesystem": "4.2.3",
@@ -83,7 +83,7 @@ export const createRuntimeUsingPlaywright = ({
83
83
  stopOnExit: true,
84
84
  playwrightLaunchOptions: {
85
85
  ...playwrightLaunchOptions,
86
- headless: !headful,
86
+ headless: headful === undefined ? !keepRunning : !headful,
87
87
  },
88
88
  })
89
89
  if (browser._initializer.version) {