@camperaid/watest 2.5.0 → 2.5.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.
Files changed (166) hide show
  1. package/README.md +274 -129
  2. package/bin/watest.js +36 -2
  3. package/core/base.js +10 -3
  4. package/core/core.js +43 -15
  5. package/core/deps.js +211 -0
  6. package/core/{process_args.js → process-args.js} +8 -0
  7. package/core/series.js +70 -28
  8. package/core/settings.js +28 -10
  9. package/core/system.js +68 -0
  10. package/core/util.js +1 -1
  11. package/eslint.config.js +1 -1
  12. package/index.js +15 -3
  13. package/interfaces/servicer.js +13 -3
  14. package/logging/logging.js +1 -1
  15. package/logging/logpipe.js +38 -21
  16. package/package.json +1 -1
  17. package/tests/base/{t_core.js → t-core.js} +10 -3
  18. package/tests/base/t-system.js +59 -0
  19. package/tests/base/{t_throws.js → t-throws.js} +67 -0
  20. package/tests/base/test.js +1 -2
  21. package/tests/deps/samples/nested/.watestrc.js +3 -0
  22. package/tests/deps/samples/nested/tests/meta.js +1 -0
  23. package/tests/deps/samples/nested/tests/services/meta.js +1 -0
  24. package/tests/deps/samples/nested/tests/services/ws/meta.js +1 -0
  25. package/tests/deps/samples/nested/tests/services/ws/webservice/meta.js +2 -0
  26. package/tests/deps/samples/nested/tests/services/ws/webservice/t-ws.js +3 -0
  27. package/tests/deps/samples/unified/.watestrc.js +3 -0
  28. package/tests/deps/samples/unified/tests/e2e/meta.js +4 -0
  29. package/tests/deps/samples/unified/tests/e2e/pages/meta.js +1 -0
  30. package/tests/deps/samples/unified/tests/e2e/pages/t-example.js +3 -0
  31. package/tests/deps/samples/unified/tests/e2e/t-example.js +3 -0
  32. package/tests/deps/samples/unified/tests/integration/meta.js +3 -0
  33. package/tests/deps/samples/unified/tests/lib/meta.js +0 -0
  34. package/tests/deps/samples/unified/tests/lib/t-example.js +3 -0
  35. package/tests/deps/samples/unified/tests/meta.js +8 -0
  36. package/tests/deps/samples/unified/tests/services/meta.js +3 -0
  37. package/tests/deps/samples/unified/tests/services/request/meta.js +1 -0
  38. package/tests/deps/samples/unified/tests/services/t-example.js +3 -0
  39. package/tests/deps/t-parse-cell-syntax.js +6 -0
  40. package/tests/deps/t-parse-grid-args.js +11 -0
  41. package/tests/deps/t-watest-deps.js +37 -0
  42. package/tests/deps/t-watest-grid.js +122 -0
  43. package/tests/deps/test.js +63 -0
  44. package/tests/e2e/samples/folder/package-lock.json +3 -1
  45. package/tests/e2e/samples/loader/package-lock.json +3 -1
  46. package/tests/e2e/samples/loader/tests/meta.js +1 -1
  47. package/tests/e2e/samples/{loader_mixed → loader-mixed}/package-lock.json +3 -1
  48. package/tests/e2e/samples/{loader_multiple/tests/core → loader-mixed/tests/ui}/meta.js +1 -1
  49. package/tests/e2e/samples/{loader_multiple → loader-multiple}/package-lock.json +3 -1
  50. package/tests/e2e/samples/{loader_multiple → loader-multiple}/tests/base/meta.js +1 -1
  51. package/tests/e2e/samples/{loader_mixed/tests/ui → loader-multiple/tests/core}/meta.js +1 -1
  52. package/tests/e2e/samples/single/package-lock.json +3 -1
  53. package/tests/e2e/samples/{wd_mixed → wd-mixed}/package-lock.json +3 -1
  54. package/tests/e2e/samples/{wd_single → wd-single}/package-lock.json +3 -1
  55. package/tests/e2e/{t_folder.js → t-folder.js} +3 -3
  56. package/tests/e2e/{t_loader_mixed.js → t-loader-mixed.js} +7 -7
  57. package/tests/e2e/{t_loader_multiple_patterns.js → t-loader-multiple-patterns.js} +9 -9
  58. package/tests/e2e/{t_loader_multiple.js → t-loader-multiple.js} +8 -8
  59. package/tests/e2e/{t_loader.js → t-loader.js} +4 -4
  60. package/tests/e2e/{t_single.js → t-single.js} +3 -3
  61. package/tests/e2e/{t_wd_firefox_chrome_pattern.js → t-wd-firefox-chrome-pattern.js} +8 -8
  62. package/tests/e2e/{t_wd_firefox_chrome.js → t-wd-firefox-chrome.js} +7 -7
  63. package/tests/e2e/{t_wd_firefox.js → t-wd-firefox.js} +5 -5
  64. package/tests/e2e/{t_wd_mixed_firefox_chrome.js → t-wd-mixed-firefox-chrome.js} +9 -9
  65. package/tests/e2e/{t_wd_mixed_firefox.js → t-wd-mixed-firefox.js} +7 -7
  66. package/tests/meta.js +1 -1
  67. package/tests/series/build/t-pattern-filtering.js +175 -0
  68. package/tests/series/build/{t_webdriver_services.js → t-webdriver-services.js} +1 -0
  69. package/tests/series/logging/{t_failures.js → t-failures.js} +1 -1
  70. package/tests/series/logging/{t_success.js → t-success.js} +1 -1
  71. package/tests/series/logging/{t_verify.js → t-verify.js} +2 -2
  72. package/tests/series/meta.js +1 -0
  73. package/tests/series/perform/{t_failure_notest.js → t-failure-notest.js} +1 -0
  74. package/tests/series/perform/{t_failure.js → t-failure.js} +1 -0
  75. package/tests/series/perform/{t_intermittent_global.js → t-intermittent-global.js} +1 -0
  76. package/tests/series/perform/{t_intermittent.js → t-intermittent.js} +2 -0
  77. package/tests/series/perform/{t_missing_perma.js → t-missing-perma.js} +2 -0
  78. package/tests/series/perform/{t_nested.js → t-nested.js} +1 -0
  79. package/tests/series/perform/{t_perma.js → t-perma.js} +1 -0
  80. package/tests/series/perform/{t_success.js → t-success.js} +2 -0
  81. package/tests/series/servicer/mock-servicer.js +68 -0
  82. package/tests/series/servicer/t-nested-servicer-lifecycle.js +99 -0
  83. package/tests/series/servicer/t-servicer-no-services.js +53 -0
  84. package/tests/series/servicer/t-servicer-type-switching.js +139 -0
  85. package/tests/series/servicer/t-servicer.js +51 -0
  86. package/tests/series/test.js +1 -1
  87. package/tests/test.js +2 -0
  88. package/tests/webdriver/test.js +3 -3
  89. package/webdriver/{control_driver.js → control-driver.js} +1 -1
  90. package/webdriver/{driver_base.js → driver-base.js} +3 -1
  91. package/webdriver/driver.js +1 -1
  92. package/webdriver/session.js +57 -30
  93. package/tests/base/{t_api.js → t-api.js} +0 -0
  94. package/tests/base/{t_contains.js → t-contains.js} +0 -0
  95. package/tests/base/{t_format.js → t-format.js} +0 -0
  96. package/tests/base/{t_is_object.js → t-is-object.js} +0 -0
  97. package/tests/base/{t_is_primitive.js → t-is-primitive.js} +0 -0
  98. package/tests/base/{t_is_string.js → t-is-string.js} +0 -0
  99. package/tests/base/{t_is.js → t-is.js} +0 -0
  100. package/tests/base/{t_ok.js → t-ok.js} +0 -0
  101. package/tests/base/{t_stringify.js → t-stringify.js} +0 -0
  102. package/tests/base/{t_test_.js → t-test-.js} +0 -0
  103. package/tests/e2e/samples/folder/tests/unit/{t_test.js → t-test.js} +0 -0
  104. package/tests/e2e/samples/{loader_multiple/tests/module_mock.js → loader/tests/module-mock.js} +0 -0
  105. package/tests/e2e/samples/loader/tests/{t_test.js → t-test.js} +0 -0
  106. package/tests/e2e/samples/{loader_mixed → loader-mixed}/.watestrc.js +0 -0
  107. package/tests/e2e/samples/{loader_mixed → loader-mixed}/package.json +0 -0
  108. package/tests/e2e/samples/{loader_mixed → loader-mixed}/tests/meta.js +0 -0
  109. package/tests/e2e/samples/{loader/tests/module_mock.js → loader-mixed/tests/module-mock.js} +0 -0
  110. package/tests/e2e/samples/{loader_mixed → loader-mixed}/tests/module.js +0 -0
  111. package/tests/e2e/samples/{loader_mixed/tests/ui/t_test.js → loader-mixed/tests/ui/t-test.js} +0 -0
  112. package/tests/e2e/samples/{loader_mixed/tests/unit/t_test.js → loader-mixed/tests/unit/t-test.js} +0 -0
  113. package/tests/e2e/samples/{loader_multiple → loader-multiple}/.watestrc.js +0 -0
  114. package/tests/e2e/samples/{loader_multiple → loader-multiple}/package.json +0 -0
  115. package/tests/e2e/samples/{loader_multiple/tests/base/t_btest.js → loader-multiple/tests/base/t-btest.js} +0 -0
  116. package/tests/e2e/samples/{loader_multiple/tests/core/t_ctest.js → loader-multiple/tests/core/t-ctest.js} +0 -0
  117. package/tests/e2e/samples/{loader_multiple → loader-multiple}/tests/meta.js +0 -0
  118. package/tests/e2e/samples/{loader_mixed/tests/module_mock.js → loader-multiple/tests/module-mock.js} +0 -0
  119. package/tests/e2e/samples/{loader_multiple → loader-multiple}/tests/module.js +0 -0
  120. package/tests/e2e/samples/single/tests/{t_test.js → t-test.js} +0 -0
  121. package/tests/e2e/samples/{wd_mixed → wd-mixed}/.watestrc.js +0 -0
  122. package/tests/e2e/samples/{wd_mixed → wd-mixed}/package.json +0 -0
  123. package/tests/e2e/samples/{wd_mixed → wd-mixed}/tests/meta.js +0 -0
  124. package/tests/e2e/samples/{wd_mixed → wd-mixed}/tests/ui/meta.js +0 -0
  125. package/tests/e2e/samples/{wd_mixed/tests/ui/t_test.js → wd-mixed/tests/ui/t-test.js} +0 -0
  126. package/tests/e2e/samples/{wd_mixed/tests/unit/t_test.js → wd-mixed/tests/unit/t-test.js} +0 -0
  127. package/tests/e2e/samples/{wd_single → wd-single}/.watestrc.js +0 -0
  128. package/tests/e2e/samples/{wd_single → wd-single}/package.json +0 -0
  129. package/tests/e2e/samples/{wd_single → wd-single}/tests/meta.js +0 -0
  130. package/tests/e2e/samples/{wd_single/tests/t_test.js → wd-single/tests/t-test.js} +0 -0
  131. package/tests/series/build/{t_adjust_names_webdriver.js → t-adjust-names-webdriver.js} +0 -0
  132. package/tests/series/build/{t_adjust_names.js → t-adjust-names.js} +0 -0
  133. package/tests/series/build/{t_expected_failures.js → t-expected-failures.js} +0 -0
  134. package/tests/series/build/{t_loader_mixed.js → t-loader-mixed.js} +0 -0
  135. package/tests/series/build/{t_loader.js → t-loader.js} +0 -0
  136. package/tests/series/build/{t_mixed.js → t-mixed.js} +0 -0
  137. package/tests/series/build/{t_nested.js → t-nested.js} +0 -0
  138. package/tests/series/build/{t_patterns_loader.js → t-patterns-loader.js} +0 -0
  139. package/tests/series/build/{t_patterns_webdriver.js → t-patterns-webdriver.js} +0 -0
  140. package/tests/series/build/{t_webdriver_firefox_mixed.js → t-webdriver-firefox-mixed.js} +0 -0
  141. package/tests/series/build/{t_webdriver_nested.js → t-webdriver-nested.js} +0 -0
  142. package/tests/series/build/{t_webdriver.js → t-webdriver.js} +0 -0
  143. package/tests/series/generic/{t_failures_info.js → t-failures-info.js} +0 -0
  144. package/tests/series/{mock_series.js → mock-series.js} +0 -0
  145. package/tests/series/run/{t_debunk_failure.js → t-debunk-failure.js} +1 -1
  146. package/tests/series/run/{t_debunk_success.js → t-debunk-success.js} +1 -1
  147. package/tests/series/run/{t_nested.js → t-nested.js} +1 -1
  148. package/tests/series/run/{t_verify_webdriver.js → t-verify-webdriver.js} +1 -1
  149. package/tests/series/run/{t_verify.js → t-verify.js} +1 -1
  150. /package/tests/webdriver/{t_app_driver_selectors.js → t-app-driver-selectors.js} +0 -0
  151. /package/tests/webdriver/{t_app_driver.js → t-app-driver.js} +0 -0
  152. /package/tests/webdriver/{t_attribute_all.js → t-attribute-all.js} +0 -0
  153. /package/tests/webdriver/{t_attribute.js → t-attribute.js} +0 -0
  154. /package/tests/webdriver/{t_doubleclick.js → t-doubleclick.js} +0 -0
  155. /package/tests/webdriver/{t_doubleclickat.js → t-doubleclickat.js} +0 -0
  156. /package/tests/webdriver/{t_execute.js → t-execute.js} +0 -0
  157. /package/tests/webdriver/{t_if_has_elements.js → t-if-has-elements.js} +0 -0
  158. /package/tests/webdriver/{t_if_no_elements.js → t-if-no-elements.js} +0 -0
  159. /package/tests/webdriver/{t_no_elements_or_not_visible.js → t-no-elements-or-not-visible.js} +0 -0
  160. /package/tests/webdriver/{t_properties.js → t-properties.js} +0 -0
  161. /package/tests/webdriver/{t_script.js → t-script.js} +0 -0
  162. /package/tests/webdriver/{t_select_all.js → t-select-all.js} +0 -0
  163. /package/tests/webdriver/{t_selection.js → t-selection.js} +0 -0
  164. /package/tests/webdriver/{t_text_all.js → t-text-all.js} +0 -0
  165. /package/tests/webdriver/{t_text.js → t-text.js} +0 -0
  166. /package/webdriver/{app_driver.js → app-driver.js} +0 -0
@@ -2,7 +2,7 @@ import path from 'path';
2
2
 
3
3
  import { FileStream as DefaultFileStream } from './filestream.js';
4
4
  import { log, log_error } from './logging.js';
5
- import settings from '../core/settings.js';
5
+ import { settings } from '../core/settings.js';
6
6
 
7
7
  /**
8
8
  * A single instance of a logpipe writing to std and file streams.
@@ -15,6 +15,7 @@ class LogPipeInstance {
15
15
 
16
16
  attach(invocation) {
17
17
  this.invocation = invocation;
18
+ this.run = settings.run;
18
19
  this.log_dir = path.join(settings.log_dir, this.invocation);
19
20
  if (this.suppress_logging) {
20
21
  return;
@@ -28,7 +29,7 @@ class LogPipeInstance {
28
29
  log_error(e);
29
30
  });
30
31
 
31
- return settings.logger.testRunStarted({ run: settings.run, invocation });
32
+ return settings.logger.testRunStarted({ run: this.run, invocation });
32
33
  }
33
34
 
34
35
  async logScreenshot(pic) {
@@ -41,7 +42,7 @@ class LogPipeInstance {
41
42
  log(`Screenshot is captured and written to ${stream.filepath}`);
42
43
 
43
44
  await settings.logger.writeLogFile({
44
- run: settings.run,
45
+ run: this.run,
45
46
  invocation: this.invocation,
46
47
  name,
47
48
  content,
@@ -50,30 +51,42 @@ class LogPipeInstance {
50
51
 
51
52
  logSourceMap() {
52
53
  return settings.logger.writeSourceMap({
53
- run: settings.run,
54
+ run: this.run,
54
55
  invocation: this.invocation,
55
56
  });
56
57
  }
57
58
 
58
59
  release() {
59
- return (
60
- !this.suppress_logging &&
61
- this.fstream
62
- .end()
63
- .then(() => this.fstream.readFile())
64
- .then(content =>
65
- settings.logger.writeLogFile({
66
- run: settings.run,
60
+ if (this.suppress_logging || !this.fstream) {
61
+ return Promise.resolve();
62
+ }
63
+
64
+ const filepath = this.fstream.filepath;
65
+
66
+ return this.fstream
67
+ .end()
68
+ .then(() => this.fstream.readFile())
69
+ .then(content =>
70
+ settings.logger.writeLogFile({
71
+ run: this.run,
72
+ invocation: this.invocation,
73
+ name: this.fname,
74
+ content,
75
+ zip: true,
76
+ }),
77
+ )
78
+ .catch(e => {
79
+ log_error(
80
+ `[logpipe:error] ✗ Logging shutdown failed for ${filepath}:`,
81
+ {
82
+ filepath,
67
83
  invocation: this.invocation,
68
- name: this.fname,
69
- content,
70
- zip: true,
71
- }),
72
- )
73
- .catch(e => {
74
- log_error(`Logging shutdown rejected: ${e}`);
75
- })
76
- );
84
+ error: e.message,
85
+ code: e.code,
86
+ stack: e.stack,
87
+ },
88
+ );
89
+ });
77
90
  }
78
91
  }
79
92
 
@@ -127,6 +140,10 @@ class LogPipe {
127
140
  // Remove the pipe from the stack to prevent anyone writing into it after
128
141
  // its release.
129
142
  let pipeToRelease = this.stack.pop();
143
+ if (!pipeToRelease) {
144
+ return Promise.resolve();
145
+ }
146
+
130
147
  await pipeToRelease.release(...args);
131
148
 
132
149
  if (this.suppress_logging) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camperaid/watest",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "Web Application Testsuite",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,14 +1,21 @@
1
1
  import { colorify, group, fail, success, is_output } from './test.js';
2
2
 
3
3
  export async function test() {
4
- // console.log
5
- await is_output(() => console.log(3), [`3\n`], [], `console.log(3)`);
4
+ // console.log - Node adds yellow color codes to numbers in TTY
5
+ const colored3 = `\x1b[33m3\x1b[39m\n`;
6
+
7
+ await is_output(
8
+ () => console.log(3),
9
+ [colored3],
10
+ [],
11
+ `console.log(3)`,
12
+ );
6
13
 
7
14
  // console.error
8
15
  await is_output(
9
16
  () => console.error(3),
10
17
  [],
11
- [`3\n`],
18
+ [colored3],
12
19
  `console.error(3)`,
13
20
  );
14
21
 
@@ -0,0 +1,59 @@
1
+ import { ok, contains } from '../../index.js';
2
+ import { runCommand, runBashScript, execCommand } from '../../core/system.js';
3
+
4
+ export async function test() {
5
+ // Test 1: runCommand with echo
6
+ {
7
+ const result = await runCommand('echo', ['hello', 'world']);
8
+ ok(result.exitCode === 0, 'echo command should succeed');
9
+ ok(result.stdout === 'hello world', 'stdout should contain echo output');
10
+ ok(result.stderr === '', 'stderr should be empty');
11
+ ok(result.stdoutLines.length === 1, 'should have one stdout line');
12
+ ok(result.stdoutLines[0] === 'hello world', 'stdout line should match');
13
+ }
14
+
15
+ // Test 2: runBashScript with simple script
16
+ {
17
+ const script = 'echo "test script"; echo "line 2"';
18
+ const result = await runBashScript(script);
19
+ ok(result.exitCode === 0, 'bash script should succeed');
20
+ contains(result.stdout, 'test script', 'should contain first echo');
21
+ contains(result.stdout, 'line 2', 'should contain second echo');
22
+ ok(result.stdoutLines.length === 2, 'should have two stdout lines');
23
+ }
24
+
25
+ // Test 3: execCommand for simple cases
26
+ {
27
+ const output = await execCommand('echo', ['simple test']);
28
+ ok(output === 'simple test', 'execCommand should return stdout directly');
29
+ }
30
+
31
+ // Test 4: runCommand with failing command
32
+ {
33
+ const result = await runCommand('bash', ['-c', 'echo "error" >&2; exit 1']);
34
+ ok(result.exitCode === 1, 'failing command should return exit code 1');
35
+ ok(result.stdout === '', 'stdout should be empty');
36
+ ok(result.stderr === 'error', 'stderr should contain error message');
37
+ }
38
+
39
+ // Test 5: execCommand should throw on failure
40
+ {
41
+ let threw = false;
42
+ try {
43
+ await execCommand('bash', ['-c', 'exit 1']);
44
+ } catch (error) {
45
+ threw = true;
46
+ contains(error.message, 'exit code 1', 'error should mention exit code');
47
+ }
48
+ ok(threw, 'execCommand should throw on non-zero exit code');
49
+ }
50
+
51
+ // Test 6: runBashScript with arguments
52
+ {
53
+ const script = 'echo "arg1: $1, arg2: $2"';
54
+ const result = await runBashScript(script, ['first', 'second']);
55
+ ok(result.exitCode === 0, 'bash script with args should succeed');
56
+ contains(result.stdout, 'arg1: first', 'should pass first argument');
57
+ contains(result.stdout, 'arg2: second', 'should pass second argument');
58
+ }
59
+ }
@@ -136,4 +136,71 @@ unexpected character: '2' at 6 pos, expected: '1' at '' line
136
136
  ],
137
137
  `no_throws fail (async)`,
138
138
  );
139
+
140
+ // throws: accept object descriptor (statusCode + JSON body)
141
+ await is_output(
142
+ () =>
143
+ throws(
144
+ () => {
145
+ const err = new Error(
146
+ 'HTTP error code 422 for https://api.digitalocean.com/v2/droplets',
147
+ );
148
+ err.statusCode = 422;
149
+ err.responseBody = JSON.stringify({
150
+ id: 'unprocessable_entity',
151
+ message: 'You specified an invalid image for Droplet creation.',
152
+ });
153
+ throw err;
154
+ },
155
+ {
156
+ statusCode: 422,
157
+ responseBody: JSON.stringify({
158
+ id: 'unprocessable_entity',
159
+ message: 'You specified an invalid image for Droplet creation.',
160
+ }),
161
+ },
162
+ `Throws error descriptor`,
163
+ ),
164
+ [
165
+ `Ok: Throws error descriptor, got: {statusCode: 422, responseBody: '{"id":"unprocessable_entity","message":"You specified an invalid image for Droplet creation."}'}`,
166
+ ],
167
+ [],
168
+ `throws accept object descriptor`,
169
+ );
170
+
171
+ // throws: accept RegExp for message matching
172
+ await is_output(
173
+ () =>
174
+ throws(
175
+ () => {
176
+ throw new Error(
177
+ 'Unexpected 404 response code for https://example.com/test from droplet 1',
178
+ );
179
+ },
180
+ /404|Unexpected/,
181
+ `Throws with RegExp`,
182
+ ),
183
+ [
184
+ `Ok: Throws with RegExp 'Unexpected 404 response code for https://example.com/test from droplet 1' matches /404|Unexpected/ regexp`,
185
+ ],
186
+ [],
187
+ `throws accept RegExp pattern`,
188
+ );
189
+
190
+ // throws: fail RegExp that doesn't match
191
+ await is_output(
192
+ () =>
193
+ throws(
194
+ () => {
195
+ throw new Error('Some other error message');
196
+ },
197
+ /404|Unexpected/,
198
+ `RegExp should match`,
199
+ ),
200
+ [],
201
+ [
202
+ `Failed: RegExp should match 'Some other error message' doesn't match /404|Unexpected/ regexp`,
203
+ ],
204
+ `throws fail RegExp no match`,
205
+ );
139
206
  }
@@ -1,8 +1,7 @@
1
1
  import { testflow } from '../../core/core.js';
2
2
  import { is_output as is_output_base } from '../../core/base.js';
3
3
 
4
- export * from '../../core/base.js';
5
- export * from '../../core/core.js';
4
+ export * from '../test.js';
6
5
  export * from '../../core/format.js';
7
6
 
8
7
  function is_output(func, out, err, msg) {
@@ -0,0 +1,3 @@
1
+ export default {
2
+ tests_folder: 'tests',
3
+ };
@@ -0,0 +1 @@
1
+ export const folders = ['services'];
@@ -0,0 +1 @@
1
+ export const folders = ['ws'];
@@ -0,0 +1 @@
1
+ export const folders = ['webservice'];
@@ -0,0 +1,2 @@
1
+ export const servicer = 'docker';
2
+ export const services = ['ws'];
@@ -0,0 +1,3 @@
1
+ export async function test() {
2
+ // Dummy test
3
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ webdrivers: ['chrome', 'firefox'],
3
+ };
@@ -0,0 +1,4 @@
1
+ export const servicer = 'kubernetes';
2
+ export const webdriver = true;
3
+ export const services = ['db', ['nginx', { env: { CA_TEST: 'value' } }]];
4
+ export const folders = ['pages'];
@@ -0,0 +1 @@
1
+ export const services = ['request'];
@@ -0,0 +1,3 @@
1
+ export async function test() {
2
+ // Dummy test
3
+ }
@@ -0,0 +1,3 @@
1
+ export async function test() {
2
+ // Dummy test
3
+ }
@@ -0,0 +1,3 @@
1
+ export const webdriver = true;
2
+ export const servicer = 'docker';
3
+ export const services = ['db'];
File without changes
@@ -0,0 +1,3 @@
1
+ export async function test() {
2
+ // Dummy test
3
+ }
@@ -0,0 +1,8 @@
1
+ export const folders = ['e2e', 'integration', 'lib', 'services'];
2
+
3
+ export const grid = {
4
+ 'e2e+': ['tests/e2e'],
5
+ 'integration': ['tests/integration'],
6
+ 'lib': ['tests/lib'],
7
+ 'services': ['tests/services'],
8
+ };
@@ -0,0 +1,3 @@
1
+ export const servicer = 'docker';
2
+ export const services = ['inbucket'];
3
+ export const folders = ['request'];
@@ -0,0 +1 @@
1
+ export const services = ['request'];
@@ -0,0 +1,3 @@
1
+ export async function test() {
2
+ // Dummy test
3
+ }
@@ -0,0 +1,6 @@
1
+ import { is, parseCellSyntax } from './test.js';
2
+
3
+ export async function test() {
4
+ is(parseCellSyntax('e2e'), { name: 'e2e', split: false }, 'without +');
5
+ is(parseCellSyntax('e2e+'), { name: 'e2e', split: true }, 'with +');
6
+ }
@@ -0,0 +1,11 @@
1
+ import { is, parseGridArgs } from './test.js';
2
+
3
+ export async function test() {
4
+ const args1 = parseGridArgs(['tests/e2e', 'tests/www', 'chrome', 'firefox']);
5
+ is(args1.paths, ['tests/e2e', 'tests/www'], 'paths');
6
+ is(args1.webdrivers, ['chrome', 'firefox'], 'webdrivers');
7
+
8
+ const args2 = parseGridArgs([]);
9
+ is(args2.paths, [], 'empty paths');
10
+ is(args2.webdrivers, [], 'empty webdrivers');
11
+ }
@@ -0,0 +1,37 @@
1
+ import { testDeps } from './test.js';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const unifiedSamplePath = path.join(__dirname, 'samples/unified');
7
+ const nestedSamplePath = path.join(__dirname, 'samples/nested');
8
+
9
+ export async function test() {
10
+ // Test specific path: tests/e2e
11
+ await testDeps(['tests/e2e'], unifiedSamplePath, {
12
+ servicers: ['kubernetes'],
13
+ webdriver: true,
14
+ services: ['db', 'nginx', 'request'],
15
+ });
16
+
17
+ // Test no arguments (should default to tests/)
18
+ await testDeps([], unifiedSamplePath, {
19
+ servicers: ['kubernetes', 'docker'],
20
+ webdriver: true,
21
+ services: ['db', 'nginx', 'request', 'inbucket'],
22
+ });
23
+
24
+ // Test with deeply nested test file path
25
+ await testDeps(['tests/services/ws/webservice/t-ws.js'], nestedSamplePath, {
26
+ servicers: ['docker'],
27
+ webdriver: false,
28
+ services: ['ws'],
29
+ });
30
+
31
+ // Test with nested directory path
32
+ await testDeps(['tests/services/ws/webservice'], nestedSamplePath, {
33
+ servicers: ['docker'],
34
+ webdriver: false,
35
+ services: ['ws'],
36
+ });
37
+ }
@@ -0,0 +1,122 @@
1
+ import { is, testGrid } from './test.js';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const samplesPath = path.join(__dirname, 'samples/unified');
7
+
8
+ export async function test() {
9
+ // Default browsers from .watestrc.js (chrome, firefox) - splits + cells
10
+ const result1 = await testGrid([], samplesPath);
11
+ is(
12
+ result1,
13
+ {
14
+ 'e2e-chrome': {
15
+ paths: ['tests/e2e'],
16
+ webdrivers: 'chrome',
17
+ servicers: ['kubernetes'],
18
+ services: ['db', 'nginx', 'request'],
19
+ },
20
+ 'e2e-firefox': {
21
+ paths: ['tests/e2e'],
22
+ webdrivers: 'firefox',
23
+ servicers: ['kubernetes'],
24
+ services: ['db', 'nginx', 'request'],
25
+ },
26
+ 'integration': {
27
+ paths: ['tests/integration'],
28
+ webdrivers: 'chrome firefox',
29
+ servicers: ['docker'],
30
+ services: ['db'],
31
+ },
32
+ 'lib': {
33
+ paths: ['tests/lib'],
34
+ webdrivers: '',
35
+ servicers: [],
36
+ services: [],
37
+ },
38
+ 'services': {
39
+ paths: ['tests/services'],
40
+ webdrivers: '',
41
+ servicers: ['docker'],
42
+ services: ['inbucket', 'request'],
43
+ },
44
+ },
45
+ 'default browsers split + cells',
46
+ );
47
+
48
+ // Single browser - no split even for + cells
49
+ const result2 = await testGrid(['chrome'], samplesPath);
50
+ is(
51
+ result2,
52
+ {
53
+ e2e: {
54
+ paths: ['tests/e2e'],
55
+ webdrivers: 'chrome',
56
+ servicers: ['kubernetes'],
57
+ services: ['db', 'nginx', 'request'],
58
+ },
59
+ integration: {
60
+ paths: ['tests/integration'],
61
+ webdrivers: 'chrome',
62
+ servicers: ['docker'],
63
+ services: ['db'],
64
+ },
65
+ lib: {
66
+ paths: ['tests/lib'],
67
+ webdrivers: '',
68
+ servicers: [],
69
+ services: [],
70
+ },
71
+ services: {
72
+ paths: ['tests/services'],
73
+ webdrivers: '',
74
+ servicers: ['docker'],
75
+ services: ['inbucket', 'request'],
76
+ },
77
+ },
78
+ 'single browser no split',
79
+ );
80
+
81
+ // Multiple browsers without split (no + cells, explicit browsers)
82
+ const result3 = await testGrid(
83
+ ['tests/lib', 'chrome', 'firefox'],
84
+ samplesPath,
85
+ );
86
+ is(
87
+ result3,
88
+ {
89
+ lib: {
90
+ paths: ['tests/lib'],
91
+ webdrivers: '',
92
+ servicers: [],
93
+ services: [],
94
+ },
95
+ },
96
+ 'no webdriver cell with multiple browsers',
97
+ );
98
+
99
+ // Filter by paths with multiple browsers (+ cell splits)
100
+ const result4 = await testGrid(
101
+ ['tests/e2e', 'chrome', 'firefox'],
102
+ samplesPath,
103
+ );
104
+ is(
105
+ result4,
106
+ {
107
+ 'e2e-chrome': {
108
+ paths: ['tests/e2e'],
109
+ webdrivers: 'chrome',
110
+ servicers: ['kubernetes'],
111
+ services: ['db', 'nginx', 'request'],
112
+ },
113
+ 'e2e-firefox': {
114
+ paths: ['tests/e2e'],
115
+ webdrivers: 'firefox',
116
+ servicers: ['kubernetes'],
117
+ services: ['db', 'nginx', 'request'],
118
+ },
119
+ },
120
+ 'filtered paths with split',
121
+ );
122
+ }
@@ -0,0 +1,63 @@
1
+ export * from '../test.js';
2
+ export * from '../../core/deps.js';
3
+
4
+ import { is } from '../test.js';
5
+ import { spawn } from '../../core/spawn.js';
6
+ import path from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+
11
+ /**
12
+ * Helper to test watest --deps command
13
+ * @param {string[]} paths - Paths to pass to --deps (empty array for no args)
14
+ * @param {string} samplesPath - Path to samples directory
15
+ * @param {Object} expected - Expected metadata result
16
+ */
17
+ export async function testDeps(paths, samplesPath, expected) {
18
+ let stdout = '';
19
+ let stderr = '';
20
+
21
+ const args = ['../../../../bin/watest.js', '--deps', ...paths];
22
+
23
+ await spawn('node', args, { cwd: samplesPath }, buffer => {
24
+ for (let { str_data, is_stdout } of buffer) {
25
+ if (is_stdout) stdout += str_data;
26
+ else stderr += str_data;
27
+ }
28
+ });
29
+
30
+ is(stderr, '', 'no errors');
31
+
32
+ // Filter out Settings lines before JSON
33
+ const jsonStart = stdout.indexOf('{');
34
+ const jsonStr = jsonStart >= 0 ? stdout.slice(jsonStart) : stdout;
35
+
36
+ const meta = JSON.parse(jsonStr);
37
+ const pathsDesc =
38
+ paths.length === 0 ? 'no args (defaults to tests/)' : paths.join(', ');
39
+ is(meta, expected, `deps for ${pathsDesc}`);
40
+ }
41
+
42
+ /**
43
+ * Helper to test watest --grid command
44
+ * @param {string[]} args - Arguments to pass to --grid
45
+ * @param {string} cwd - Working directory for the command
46
+ * @returns {Promise<Object>} Parsed grid JSON output
47
+ */
48
+ export async function testGrid(args, cwd) {
49
+ let stdout = '';
50
+ const watestBin = path.join(__dirname, '../../bin/watest.js');
51
+
52
+ await spawn('node', [watestBin, '--grid', ...args], { cwd }, buffer => {
53
+ for (let { str_data, is_stdout } of buffer) {
54
+ if (is_stdout) stdout += str_data;
55
+ }
56
+ });
57
+
58
+ const jsonStart = stdout.indexOf('{');
59
+ if (jsonStart < 0) {
60
+ throw new Error(`No JSON in output: ${stdout}`);
61
+ }
62
+ return JSON.parse(stdout.slice(jsonStart));
63
+ }
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.10",
19
+ "version": "2.5.1",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -187,6 +187,7 @@
187
187
  "version": "8.12.1",
188
188
  "dev": true,
189
189
  "license": "MIT",
190
+ "peer": true,
190
191
  "bin": {
191
192
  "acorn": "bin/acorn"
192
193
  },
@@ -450,6 +451,7 @@
450
451
  "version": "9.8.0",
451
452
  "dev": true,
452
453
  "license": "MIT",
454
+ "peer": true,
453
455
  "dependencies": {
454
456
  "@eslint-community/eslint-utils": "^4.2.0",
455
457
  "@eslint-community/regexpp": "^4.11.0",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.4.10",
19
+ "version": "2.5.1",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -187,6 +187,7 @@
187
187
  "version": "8.12.1",
188
188
  "dev": true,
189
189
  "license": "MIT",
190
+ "peer": true,
190
191
  "bin": {
191
192
  "acorn": "bin/acorn"
192
193
  },
@@ -450,6 +451,7 @@
450
451
  "version": "9.8.0",
451
452
  "dev": true,
452
453
  "license": "MIT",
454
+ "peer": true,
453
455
  "dependencies": {
454
456
  "@eslint-community/eslint-utils": "^4.2.0",
455
457
  "@eslint-community/regexpp": "^4.11.0",
@@ -3,7 +3,7 @@ export const loader = true;
3
3
  export async function resolve(specifier, context, nextResolve) {
4
4
  switch (specifier) {
5
5
  case './module.js':
6
- specifier = './module_mock.js';
6
+ specifier = './module-mock.js';
7
7
  break;
8
8
  }
9
9
  return nextResolve(specifier, context);