@camperaid/watest 2.4.8 → 2.4.10

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/.watestrc.js CHANGED
@@ -32,6 +32,12 @@ const cfg = {
32
32
  */
33
33
  webdriver_headless: process.env.WATEST_WEBDRIVER_HEADLESS,
34
34
 
35
+ /**
36
+ * Web driver window size.
37
+ */
38
+ webdriver_window_width: process.env.WATEST_WEBDRIVER_WINDOW_WIDTH,
39
+ webdriver_window_height: process.env.WATEST_WEBDRIVER_WINDOW_HEIGHT,
40
+
35
41
  /**
36
42
  * Web driver log level.
37
43
  */
package/core/base.js CHANGED
@@ -552,21 +552,34 @@ function is_out(got, expected, msg) {
552
552
  }
553
553
 
554
554
  function throws(func, exception, msg) {
555
- try {
556
- func();
557
- fail(`${msg}: no '${exception}' exception`);
558
- } catch (e) {
559
- is(e?.message, exception, msg);
560
- }
555
+ const on_no_exception = () => fail(`${msg}: no '${exception}' exception`);
556
+ const on_exception = e => is(e?.message, exception, msg);
557
+ return throw_internal(func, on_no_exception, on_exception);
561
558
  }
562
559
 
563
560
  function no_throws(func, msg) {
564
- try {
565
- func();
566
- success(msg);
567
- } catch (e) {
561
+ const on_no_exception = () => success(msg);
562
+ const on_exception = e => {
568
563
  log_error(e);
569
564
  fail(`${msg}, got: ${e?.message ?? ''} exception`);
565
+ };
566
+ return throw_internal(func, on_no_exception, on_exception);
567
+ }
568
+
569
+ function throw_internal(func, on_no_exception, on_exception) {
570
+ try {
571
+ const result = func();
572
+
573
+ // If result is a Promise, handle it asynchronously.
574
+ if (result instanceof Promise) {
575
+ return result.then(on_no_exception, on_exception);
576
+ }
577
+
578
+ // Sync function completed successfully (no exception)
579
+ on_no_exception();
580
+ } catch (e) {
581
+ // Sync function threw an exception
582
+ on_exception(e);
570
583
  }
571
584
  }
572
585
 
package/core/format.js CHANGED
@@ -116,9 +116,10 @@ function format_warnings(count, context) {
116
116
  return colorify('warnings', label, `Total: ${count}`);
117
117
  }
118
118
 
119
- // Node v20 wraps stderr by '\[31m' and '\[39m' characters.
120
119
  function stderr_wrap(s) {
121
- return `${s}`;
120
+ // Node v22 doesn't wraps stderr by '\[31m' and '\[39m'
121
+ // characters unlike node v20.
122
+ return s;
122
123
  }
123
124
 
124
125
  function stderr_unwrap(s) {
package/core/series.js CHANGED
@@ -907,7 +907,8 @@ class Series {
907
907
  performInChildProcess({ name, path, loader, webdriver }) {
908
908
  let args = [];
909
909
  if (loader) {
910
- args.push('--loader', loader);
910
+ // Use the new --import flag with register() API instead of deprecated --loader
911
+ args.push('--import', `data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("${loader}", pathToFileURL("./"));`);
911
912
  }
912
913
  const watest_bin = nodepath.join(__dirname, '../bin/watest.js');
913
914
  args.push(
package/core/settings.js CHANGED
@@ -95,6 +95,11 @@ class Settings {
95
95
  this.rc.webdriver_headless == 'true';
96
96
  this.webdriver_loglevel = this.rc.webdriver_loglevel;
97
97
 
98
+ this.webdriver_window_width =
99
+ parseInt(this.rc.webdriver_window_width) || 1366;
100
+ this.webdriver_window_height =
101
+ parseInt(this.rc.webdriver_window_height) || 768;
102
+
98
103
  if (this.webdrivers) {
99
104
  console.log(`Settings: ${this.webdrivers.join(', ')} webdrivers`);
100
105
  }
File without changes
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@camperaid/watest",
3
- "version": "2.4.8",
3
+ "version": "2.4.10",
4
4
  "description": "Web Application Testsuite",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=20.16.0"
7
+ "node": ">=22.0.0"
8
8
  },
9
9
  "main": "index.js",
10
10
  "bin": {
@@ -1,7 +1,7 @@
1
1
  import { is_output, throws, no_throws } from './test.js';
2
2
 
3
3
  export async function test() {
4
- // throws: success
4
+ // throws: success (sync)
5
5
  await is_output(
6
6
  () =>
7
7
  throws(
@@ -13,10 +13,25 @@ export async function test() {
13
13
  ),
14
14
  [`Ok: Throws error#1, got: Error#1`],
15
15
  [],
16
- `throws sucess`,
16
+ `throws success (sync)`,
17
17
  );
18
18
 
19
- // throws: fail, unexpected exception
19
+ // throws: success (async)
20
+ await is_output(
21
+ async () =>
22
+ await throws(
23
+ async () => {
24
+ throw new Error('Error#1');
25
+ },
26
+ `Error#1`,
27
+ `Throws error#1 async`,
28
+ ),
29
+ [`Ok: Throws error#1 async, got: Error#1`],
30
+ [],
31
+ `throws success (async)`,
32
+ );
33
+
34
+ // throws: fail, unexpected exception (sync)
20
35
  await is_output(
21
36
  () =>
22
37
  throws(
@@ -36,28 +51,65 @@ Error#1
36
51
  unexpected character: '2' at 6 pos, expected: '1' at '' line
37
52
  `,
38
53
  ],
39
- `throws fail, unexpected exception`,
54
+ `throws fail, unexpected exception (sync)`,
40
55
  );
41
56
 
42
- // throws: fail, no exception
57
+ // throws: fail, unexpected exception (async)
58
+ await is_output(
59
+ async () =>
60
+ await throws(
61
+ async () => {
62
+ throw new Error('Error#2');
63
+ },
64
+ `Error#1`,
65
+ `Wanted error#1 async`,
66
+ ),
67
+ [],
68
+ [
69
+ `Failed: Wanted error#1 async;
70
+ got:
71
+ Error#2
72
+ expected:
73
+ Error#1
74
+ unexpected character: '2' at 6 pos, expected: '1' at '' line
75
+ `,
76
+ ],
77
+ `throws fail, unexpected exception (async)`,
78
+ );
79
+
80
+ // throws: fail, no exception (sync)
43
81
  await is_output(
44
82
  () => throws(() => {}, `Error#1`, `Wanted error#1`),
45
83
  [],
46
84
  [`Failed: Wanted error#1: no 'Error#1' exception`],
47
- `throws fail, no exception`,
85
+ `throws fail, no exception (sync)`,
48
86
  );
49
87
 
50
- // no_throws(() => {}, `No exceptions`)
88
+ // throws: fail, no exception (async)
89
+ await is_output(
90
+ async () => await throws(async () => {}, `Error#1`, `Wanted error#1 async`),
91
+ [],
92
+ [`Failed: Wanted error#1 async: no 'Error#1' exception`],
93
+ `throws fail, no exception (async)`,
94
+ );
51
95
 
52
- // no_throws: success
96
+ // no_throws: success (sync)
53
97
  await is_output(
54
98
  () => no_throws(() => {}, `No exceptions`),
55
99
  [`Ok: No exceptions`],
56
100
  [],
57
- `no throws: sucess`,
101
+ `no_throws success (sync)`,
58
102
  );
59
103
 
60
- // no_throws: fail
104
+ // no_throws: success (async)
105
+ await is_output(
106
+ async () => await no_throws(async () => {}, `No exceptions async`),
107
+ [`Ok: No exceptions async`],
108
+ [],
109
+ `no_throws success (async)`,
110
+ );
111
+
112
+ // no_throws: fail (sync)
61
113
  await is_output(
62
114
  () =>
63
115
  no_throws(() => {
@@ -68,6 +120,20 @@ unexpected character: '2' at 6 pos, expected: '1' at '' line
68
120
  v => v.startsWith('Error: Error#1'),
69
121
  `Failed: No exceptions, got: Error#1 exception`,
70
122
  ],
71
- `no_throws fail`,
123
+ `no_throws fail (sync)`,
124
+ );
125
+
126
+ // no_throws: fail (async)
127
+ await is_output(
128
+ async () =>
129
+ await no_throws(async () => {
130
+ throw new Error('Error#1');
131
+ }, `No exceptions async`),
132
+ [],
133
+ [
134
+ v => v.startsWith('Error: Error#1'),
135
+ `Failed: No exceptions async, got: Error#1 exception`,
136
+ ],
137
+ `no_throws fail (async)`,
72
138
  );
73
139
  }
@@ -7,10 +7,13 @@ export * from '../../core/format.js';
7
7
 
8
8
  function is_output(func, out, err, msg) {
9
9
  return is_output_base(
10
- () => {
10
+ async () => {
11
11
  testflow.lock();
12
- func();
13
- testflow.unlock();
12
+ try {
13
+ await func();
14
+ } finally {
15
+ testflow.unlock();
16
+ }
14
17
  },
15
18
  out,
16
19
  err,
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@camperaid/watest/e2e/folder",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@camperaid/watest/e2e/folder",
9
- "version": "0.0.1",
9
+ "version": "0.0.2",
10
10
  "devDependencies": {
11
11
  "watest": "file:../../../../"
12
12
  },
13
13
  "engines": {
14
- "node": ">=14.15.1"
14
+ "node": ">=22.0.0"
15
15
  }
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.8",
19
+ "version": "2.4.10",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -35,7 +35,7 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "engines": {
38
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
39
39
  }
40
40
  },
41
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "description": "Watest E2E Test",
6
6
  "engines": {
7
- "node": ">=20.16.0"
7
+ "node": ">=22.0.0"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "watest"
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@camperaid/watest/e2e/loader",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@camperaid/watest/e2e/loader",
9
- "version": "0.0.1",
9
+ "version": "0.0.2",
10
10
  "devDependencies": {
11
11
  "watest": "file:../../../../"
12
12
  },
13
13
  "engines": {
14
- "node": ">=14.15.1"
14
+ "node": ">=20.16.0"
15
15
  }
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.8",
19
+ "version": "2.4.10",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -35,7 +35,7 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "engines": {
38
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
39
39
  }
40
40
  },
41
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -1,10 +1,10 @@
1
1
  export const loader = true;
2
2
 
3
- export async function resolve(specifier, context, defaultResolve) {
3
+ export async function resolve(specifier, context, nextResolve) {
4
4
  switch (specifier) {
5
5
  case './module.js':
6
6
  specifier = './module_mock.js';
7
7
  break;
8
8
  }
9
- return defaultResolve(specifier, context, defaultResolve);
9
+ return nextResolve(specifier, context);
10
10
  }
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.8",
19
+ "version": "2.4.10",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -35,7 +35,7 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "engines": {
38
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
39
39
  }
40
40
  },
41
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -1,10 +1,10 @@
1
1
  export const loader = true;
2
2
 
3
- export async function resolve(specifier, context, defaultResolve) {
3
+ export async function resolve(specifier, context, nextResolve) {
4
4
  switch (specifier) {
5
5
  case '../module.js':
6
6
  specifier = '../module_mock.js';
7
7
  break;
8
8
  }
9
- return defaultResolve(specifier, context, defaultResolve);
9
+ return nextResolve(specifier, context);
10
10
  }
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@camperaid/watest/e2e/loader_multiple",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@camperaid/watest/e2e/loader_multiple",
9
- "version": "0.0.1",
9
+ "version": "0.0.2",
10
10
  "devDependencies": {
11
11
  "watest": "file:../../../../"
12
12
  },
13
13
  "engines": {
14
- "node": ">=14.15.1"
14
+ "node": ">=22.0.0"
15
15
  }
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.8",
19
+ "version": "2.4.10",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -35,7 +35,7 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "engines": {
38
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
39
39
  }
40
40
  },
41
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "description": "Watest E2E Test",
6
6
  "engines": {
7
- "node": ">=20.16.0"
7
+ "node": ">=22.0.0"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "watest"
@@ -1,10 +1,10 @@
1
1
  export const loader = true;
2
2
 
3
- export async function resolve(specifier, context, defaultResolve) {
3
+ export async function resolve(specifier, context, nextResolve) {
4
4
  switch (specifier) {
5
5
  case '../module.js':
6
6
  specifier = '../module_mock.js';
7
7
  break;
8
8
  }
9
- return defaultResolve(specifier, context, defaultResolve);
9
+ return nextResolve(specifier, context);
10
10
  }
@@ -1,10 +1,10 @@
1
1
  export const loader = true;
2
2
 
3
- export async function resolve(specifier, context, defaultResolve) {
3
+ export async function resolve(specifier, context, nextResolve) {
4
4
  switch (specifier) {
5
5
  case '../module.js':
6
6
  specifier = '../module_mock.js';
7
7
  break;
8
8
  }
9
- return defaultResolve(specifier, context, defaultResolve);
9
+ return nextResolve(specifier, context);
10
10
  }
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "@camperaid/watest/e2e/single",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@camperaid/watest/e2e/single",
9
- "version": "0.0.1",
9
+ "version": "0.0.2",
10
10
  "devDependencies": {
11
11
  "watest": "file:../../../../"
12
+ },
13
+ "engines": {
14
+ "node": ">=20.16.0"
12
15
  }
13
16
  },
14
17
  "../../../..": {
15
18
  "name": "@camperaid/watest",
16
- "version": "2.4.8",
19
+ "version": "2.4.10",
17
20
  "dev": true,
18
21
  "license": "MPL",
19
22
  "dependencies": {
@@ -32,7 +35,7 @@
32
35
  "prettier": "^3.3.3"
33
36
  },
34
37
  "engines": {
35
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
36
39
  }
37
40
  },
38
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@camperaid/watest/e2e/wd_firefox_mixed",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@camperaid/watest/e2e/wd_firefox_mixed",
9
- "version": "0.0.1",
9
+ "version": "0.0.2",
10
10
  "devDependencies": {
11
11
  "watest": "file:../../../../"
12
12
  },
13
13
  "engines": {
14
- "node": ">=14.15.1"
14
+ "node": ">=20.16.0"
15
15
  }
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.8",
19
+ "version": "2.4.10",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -35,7 +35,7 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "engines": {
38
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
39
39
  }
40
40
  },
41
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@camperaid/watest/e2e/wd_firefox",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@camperaid/watest/e2e/wd_firefox",
9
- "version": "0.0.1",
9
+ "version": "0.0.2",
10
10
  "devDependencies": {
11
11
  "watest": "file:../../../../"
12
12
  },
13
13
  "engines": {
14
- "node": ">=14.15.1"
14
+ "node": ">=20.16.0"
15
15
  }
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.8",
19
+ "version": "2.4.10",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -35,7 +35,7 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "engines": {
38
- "node": ">=20.16.0"
38
+ "node": ">=22.0.0"
39
39
  }
40
40
  },
41
41
  "../../../../node_modules/@bazel/runfiles": {
@@ -1,10 +1,10 @@
1
1
  export const loader = true;
2
2
 
3
- export async function resolve(specifier, context, defaultResolve) {
3
+ export async function resolve(specifier, context, nextResolve) {
4
4
  switch (specifier) {
5
5
  case './base.mjs':
6
6
  specifier = './base_mock.mjs';
7
7
  break;
8
8
  }
9
- return defaultResolve(specifier, context, defaultResolve);
9
+ return nextResolve(specifier, context);
10
10
  }
@@ -115,6 +115,19 @@ class AppDriver {
115
115
  );
116
116
  }
117
117
 
118
+ select(selectSelector, optionSelector, optionValue, field) {
119
+ return this.chain(() =>
120
+ this.action(`${this.uiname}.select '${optionValue}' into ${field}`)
121
+ .click(selectSelector, `Open ${field} select`)
122
+ .click(optionSelector, `Select '${optionValue}' in ${field}`)
123
+ .textIs(
124
+ selectSelector,
125
+ optionValue,
126
+ `Check '${optionValue}' option selected in ${field}`,
127
+ ),
128
+ );
129
+ }
130
+
118
131
  execute(script, descr) {
119
132
  return this.chain(() =>
120
133
  this.action(`${this.uiname}.execute ${descr}`).executeScript(
@@ -802,9 +802,13 @@ class Driver extends DriverBase {
802
802
  /**
803
803
  * Press tab key.
804
804
  */
805
- tab() {
805
+ tab(tabCount = 1) {
806
806
  return this.run(
807
- () => this.dvr.actions({ bridge: true }).sendKeys(Key.TAB).perform(),
807
+ () =>
808
+ this.dvr
809
+ .actions({ bridge: true })
810
+ .sendKeys(Array(tabCount).fill(Key.TAB).join(''))
811
+ .perform(),
808
812
  `Press Tab`,
809
813
  );
810
814
  }
@@ -26,8 +26,8 @@ import chrome from 'selenium-webdriver/chrome.js';
26
26
 
27
27
  function getChromeOptions() {
28
28
  const chromeOptions = new chrome.Options().windowSize({
29
- width: 1366,
30
- height: 768,
29
+ width: settings.webdriver_window_width,
30
+ height: settings.webdriver_window_height,
31
31
  });
32
32
  if (settings.webdriver_headless) {
33
33
  chromeOptions.addArguments('headless');