@camperaid/watest 2.5.3 → 2.5.5

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.
@@ -44,6 +44,11 @@ class ProcessArgs {
44
44
  obj.rootFolder = process.argv[++i];
45
45
  break;
46
46
 
47
+ case '--rerun':
48
+ // Rerun suffix appended to first folder level (e.g., --rerun 5 → www becomes www-5)
49
+ obj.rerun = process.argv[++i];
50
+ break;
51
+
47
52
  case '--input-type=module':
48
53
  break;
49
54
 
@@ -70,6 +75,10 @@ class ProcessArgs {
70
75
  } else if ('verify' in args) {
71
76
  list.push('--verify');
72
77
  }
78
+ // Pass rerun suffix to child processes
79
+ if ('rerun' in args) {
80
+ list.push('--rerun', args.rerun);
81
+ }
73
82
  return list;
74
83
  }
75
84
  }
package/core/series.js CHANGED
@@ -76,6 +76,7 @@ class Series {
76
76
  {
77
77
  debunk,
78
78
  invocation,
79
+ rerun,
79
80
  skipOnFail,
80
81
  timeout,
81
82
  verify,
@@ -90,6 +91,7 @@ class Series {
90
91
  ) {
91
92
  this.debunk = debunk;
92
93
  this.invocation = invocation || settings.invocation;
94
+ this.rerun = rerun;
93
95
  this.patterns = patterns;
94
96
  this.skipOnFail = skipOnFail;
95
97
  this.verify = verify;
@@ -140,9 +142,9 @@ class Series {
140
142
  await this.runFor(this.patterns.map(p => ({ path: p, webdriver: '' })));
141
143
  }
142
144
  } else {
143
- // In very mode, re-run all failing tests.
145
+ // In verify mode, re-run all failing tests.
144
146
  if (this.verify && this.failures.length > 0) {
145
- await this.runFor(this.failures, '2');
147
+ await this.runFor(this.failures, '-verify');
146
148
  }
147
149
  }
148
150
  log(`Elapsed: ${Date.now() - start_time}ms`);
@@ -158,11 +160,23 @@ class Series {
158
160
  virtual_folder: this.invocation,
159
161
  });
160
162
 
161
- // Adjust names.
163
+ // Adjust names for verify mode (appends postfix like "2").
162
164
  if (name_postfix) {
163
165
  this.adjustTestNames(tests, name_postfix);
164
166
  }
165
167
 
168
+ // Apply rerun suffix to first folder level (e.g., --rerun 5 → www becomes www-5).
169
+ if (this.rerun) {
170
+ // Extract numeric ID if the argument includes a prefix (e.g. "www-12" -> "12")
171
+ let suffix = this.rerun;
172
+ // Match hyphen + digits at end of string
173
+ const match = suffix.match(/-(\d+)$/);
174
+ if (match) {
175
+ suffix = match[1];
176
+ }
177
+ this.applyRerunSuffix(tests, suffix);
178
+ }
179
+
166
180
  if (tests.length == 0) {
167
181
  log(
168
182
  colorify(
@@ -486,13 +500,17 @@ class Series {
486
500
  testname: path,
487
501
  });
488
502
 
489
- tests.push({
503
+ const testObj = {
490
504
  name,
491
505
  path,
492
506
  func: test_wrap,
493
507
  webdriver,
494
508
  failures_info,
495
- });
509
+ };
510
+ if (test_module.timeout) {
511
+ testObj.timeout = test_module.timeout;
512
+ }
513
+ tests.push(testObj);
496
514
  }
497
515
 
498
516
  // Add tests from subfolders.
@@ -586,6 +604,20 @@ class Series {
586
604
  }
587
605
  }
588
606
 
607
+ /**
608
+ * Apply rerun suffix to the first folder level in test names.
609
+ * e.g., --rerun 5 transforms linux/www/test → linux/www-5/test
610
+ */
611
+ applyRerunSuffix(tests, suffix) {
612
+ for (let test of tests) {
613
+ // Match first folder after invocation: linux/www/ → linux/www-5/
614
+ test.name = test.name.replace(/^([^/]+\/[^/]+)(\/|$)/, `$1-${suffix}$2`);
615
+ if (test.subtests) {
616
+ this.applyRerunSuffix(test.subtests, suffix);
617
+ }
618
+ }
619
+ }
620
+
589
621
  /**
590
622
  * Runs the tests.
591
623
  */
@@ -625,6 +657,7 @@ class Series {
625
657
  skip_on_fail,
626
658
  run_in_child_process,
627
659
  init_or_uninit,
660
+ timeout,
628
661
  } = test;
629
662
 
630
663
  if (stop && !name.endsWith('uninit')) {
@@ -650,6 +683,7 @@ class Series {
650
683
 
651
684
  // Invoke a test.
652
685
  log(`\n!Running: ${name}, path: ${path}\n`);
686
+
653
687
  let start_time = new Date();
654
688
  let kungFuDeathGrip = null;
655
689
  let kungFuDeathGripResolve = null;
@@ -667,21 +701,23 @@ class Series {
667
701
  try {
668
702
  this.core.setExpectedFailures(failures_info);
669
703
 
670
- // If timeout is given then race it against the test.
671
- if (settings.timeout) {
704
+ // If timeout is given (from meta.js or settings) then race it against the test.
705
+ const effectiveTimeout = timeout ?? settings.timeout;
706
+ if (effectiveTimeout) {
707
+ const timeoutMs = effectiveTimeout * 1000;
672
708
  kungFuDeathGrip = new Promise(
673
709
  resolve => (kungFuDeathGripResolve = resolve),
674
710
  ).then(value => {
675
711
  if (value != kKungFuDeathGripCancelled) {
676
712
  fail(
677
- `Test ${name} takes longer than ${settings.timeout}ms. It's either slow or never ends.`,
713
+ `Test ${name} takes longer than ${effectiveTimeout}s. It's either slow or never ends.`,
678
714
  );
679
715
  return kKungFuDeathGripTimeout;
680
716
  }
681
717
  });
682
718
  kungFuDeathGripTimer = setTimeout(
683
719
  kungFuDeathGripResolve,
684
- settings.timeout,
720
+ timeoutMs,
685
721
  );
686
722
  let retval = await Promise.race([func(), kungFuDeathGrip]);
687
723
  if (retval != kKungFuDeathGripTimeout) {
package/core/settings.js CHANGED
@@ -54,11 +54,11 @@ class Settings {
54
54
  }
55
55
 
56
56
  get testFilePattern() {
57
- return this.rc.test_file_pattern || /^t[-_]/;
57
+ return this.rc?.test_file_pattern || /^t[-_]/;
58
58
  }
59
59
 
60
60
  get timeout() {
61
- return parseInt(this.rc.timeout) || 0;
61
+ return parseInt(this.rc?.timeout) || 0;
62
62
  }
63
63
 
64
64
  setupTmpStorageDir() {
package/core/util.js CHANGED
@@ -98,12 +98,29 @@ function removeDir(path) {
98
98
  }
99
99
  }
100
100
 
101
- function is_mac() {
101
+ function isMac() {
102
102
  return process.platform == 'darwin';
103
103
  }
104
104
 
105
+ function isLinux() {
106
+ return process.platform == 'linux';
107
+ }
108
+
109
+ function isWin() {
110
+ return process.platform == 'win32';
111
+ }
112
+
105
113
  function toDataURL(html) {
106
114
  return `data:text/html,${querystring.escape(html)}`;
107
115
  }
108
116
 
109
- export { inspect, stringify, is_mac, toDataURL, removeDir, initTmpStorage };
117
+ export {
118
+ inspect,
119
+ stringify,
120
+ isMac,
121
+ isLinux,
122
+ isWin,
123
+ toDataURL,
124
+ removeDir,
125
+ initTmpStorage,
126
+ };
package/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  } from './core/base.js';
24
24
 
25
25
  import { settings } from './core/settings.js';
26
- import { inspect } from './core/util.js';
26
+ import { inspect, isMac, isLinux, isWin } from './core/util.js';
27
27
  import { AppDriver } from './webdriver/app-driver.js';
28
28
  import { ControlDriver } from './webdriver/control-driver.js';
29
29
  import { start_session, scope } from './webdriver/session.js';
@@ -48,6 +48,9 @@ export {
48
48
  is_output,
49
49
  info,
50
50
  inspect,
51
+ isLinux,
52
+ isMac,
53
+ isWin,
51
54
  not_reached,
52
55
  no_throws,
53
56
  ok,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camperaid/watest",
3
- "version": "2.5.3",
3
+ "version": "2.5.5",
4
4
  "description": "Web Application Testsuite",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,66 @@
1
+ import { is, MockSeries } from '../test.js';
2
+
3
+ export async function test() {
4
+ const test = got => got.name == 'test_wrap';
5
+ const ts = {
6
+ 'www': {
7
+ meta: {
8
+ folders: ['ui', 'api'],
9
+ },
10
+ },
11
+ 'www/ui': {
12
+ files: ['t_login.js'],
13
+ },
14
+ 'www/ui/t_login.js': {
15
+ test() {},
16
+ },
17
+ 'www/api': {
18
+ files: ['t_search.js'],
19
+ },
20
+ 'www/api/t_search.js': {
21
+ test() {},
22
+ },
23
+ };
24
+
25
+ const series = new MockSeries([], { ts });
26
+ const tests = await series.build({
27
+ patterns: [],
28
+ folder: 'www',
29
+ virtual_folder: 'linux/www',
30
+ });
31
+ series.applyRerunSuffix(tests, '5');
32
+ series.shutdown();
33
+
34
+ is(
35
+ tests,
36
+ [
37
+ {
38
+ name: 'linux/www-5/ui',
39
+ path: 'www/ui/',
40
+ subtests: [
41
+ {
42
+ name: 'linux/www-5/ui/t_login.js',
43
+ path: 'www/ui/t_login.js',
44
+ func: test,
45
+ failures_info: [],
46
+ webdriver: '',
47
+ },
48
+ ],
49
+ },
50
+ {
51
+ name: 'linux/www-5/api',
52
+ path: 'www/api/',
53
+ subtests: [
54
+ {
55
+ name: 'linux/www-5/api/t_search.js',
56
+ path: 'www/api/t_search.js',
57
+ func: test,
58
+ failures_info: [],
59
+ webdriver: '',
60
+ },
61
+ ],
62
+ },
63
+ ],
64
+ 'rerun suffix transforms linux/www → linux/www-5',
65
+ );
66
+ }
@@ -93,16 +93,16 @@ export async function test() {
93
93
  ],
94
94
  ],
95
95
  [
96
- 'mac/webdriver/chrome/end-to-end/history2/log',
96
+ 'mac/webdriver/chrome/end-to-end/history-verify/log',
97
97
  [
98
- '\x1B[38;5;99mStarted\x1B[0m mac/webdriver/chrome/end-to-end/history2',
99
- '!Running: mac/webdriver/chrome/end-to-end/history2/t_history.js, path: tests/webdriver/end-to-end/history/t_history.js',
98
+ '\x1B[38;5;99mStarted\x1B[0m mac/webdriver/chrome/end-to-end/history-verify',
99
+ '!Running: mac/webdriver/chrome/end-to-end/history-verify/t_history.js, path: tests/webdriver/end-to-end/history/t_history.js',
100
100
  '\x1B[32mOk:\x1B[0m TestoOk',
101
101
  completed_in(
102
- 'mac/webdriver/chrome/end-to-end/history2/t_history.js',
102
+ 'mac/webdriver/chrome/end-to-end/history-verify/t_history.js',
103
103
  ),
104
- '\x1B[102mmac/webdriver/chrome/end-to-end/history2\x1B[0m Total: 1',
105
- '\x1B[38;5;243mCompleted\x1B[0m mac/webdriver/chrome/end-to-end/history2',
104
+ '\x1B[102mmac/webdriver/chrome/end-to-end/history-verify\x1B[0m Total: 1',
105
+ '\x1B[38;5;243mCompleted\x1B[0m mac/webdriver/chrome/end-to-end/history-verify',
106
106
  ],
107
107
  ],
108
108
  ]
@@ -0,0 +1,107 @@
1
+ import { is_test_output } from '../test.js';
2
+ import { success } from '../../../core/core.js';
3
+ import { Core } from '../../../core/core.js';
4
+ import { Series } from '../../../core/series.js';
5
+
6
+ class MockLogPipe {
7
+ attach() {
8
+ return Promise.resolve();
9
+ }
10
+ release() {
11
+ return Promise.resolve();
12
+ }
13
+ logToFile() {}
14
+ }
15
+
16
+ function make_perform_with_timeout(tests) {
17
+ return async () => {
18
+ const series = new Series('tests/', {
19
+ core: new Core(),
20
+ LogPipe: new MockLogPipe(),
21
+ });
22
+ try {
23
+ await series.perform({ folder: 'tests/', tests });
24
+ } finally {
25
+ series.shutdown();
26
+ }
27
+ };
28
+ }
29
+
30
+ export async function test() {
31
+ // Test that timeout from meta.js causes test to fail after specified time
32
+ await is_test_output(
33
+ make_perform_with_timeout([
34
+ {
35
+ name: 't-slow.js',
36
+ path: 'tests/t-slow.js',
37
+ func: () => new Promise(resolve => setTimeout(resolve, 500)),
38
+ failures_info: [],
39
+ timeout: 0.05, // 50ms timeout in seconds
40
+ },
41
+ ]),
42
+ [
43
+ '\x1B[38;5;99mStarted\x1B[0m tests/',
44
+ '!Running: t-slow.js, path: tests/t-slow.js',
45
+ '>t-slow.js completed in',
46
+ '\x1B[38;5;243mCompleted\x1B[0m tests/',
47
+ 'Testsuite: shutdown',
48
+ ],
49
+ [
50
+ "\x1B[31mFailed:\x1B[0m Test t-slow.js takes longer than 0.05s. It's either slow or never ends.",
51
+ '\x1B[31m>t-slow.js\x1B[0m has 1 failure(s)',
52
+ ],
53
+ 'meta timeout causes test to fail',
54
+ );
55
+
56
+ // Test that test completes normally when timeout is not exceeded
57
+ await is_test_output(
58
+ make_perform_with_timeout([
59
+ {
60
+ name: 't-fast.js',
61
+ path: 'tests/t-fast.js',
62
+ func: () => {
63
+ success('fast test passes');
64
+ return Promise.resolve();
65
+ },
66
+ failures_info: [],
67
+ timeout: 1, // 1 second timeout
68
+ },
69
+ ]),
70
+ [
71
+ '\x1B[38;5;99mStarted\x1B[0m tests/',
72
+ '!Running: t-fast.js, path: tests/t-fast.js',
73
+ '\x1B[32mOk:\x1B[0m fast test passes',
74
+ '>t-fast.js completed in',
75
+ '\x1B[38;5;243mCompleted\x1B[0m tests/',
76
+ 'Testsuite: shutdown',
77
+ ],
78
+ [],
79
+ 'test completes within timeout',
80
+ );
81
+
82
+ // Test that no timeout applies when timeout is undefined
83
+ await is_test_output(
84
+ make_perform_with_timeout([
85
+ {
86
+ name: 't-no-timeout.js',
87
+ path: 'tests/t-no-timeout.js',
88
+ func: () => {
89
+ success('no timeout test passes');
90
+ return new Promise(resolve => setTimeout(resolve, 10));
91
+ },
92
+ failures_info: [],
93
+ // timeout: undefined - no timeout set
94
+ },
95
+ ]),
96
+ [
97
+ '\x1B[38;5;99mStarted\x1B[0m tests/',
98
+ '!Running: t-no-timeout.js, path: tests/t-no-timeout.js',
99
+ '\x1B[32mOk:\x1B[0m no timeout test passes',
100
+ '>t-no-timeout.js completed in',
101
+ '\x1B[38;5;243mCompleted\x1B[0m tests/',
102
+ 'Testsuite: shutdown',
103
+ ],
104
+ [],
105
+ 'test runs without timeout when not specified',
106
+ );
107
+ }
@@ -69,11 +69,11 @@ export async function test() {
69
69
  '\x1B[38;5;99mStarted\x1B[0m mac/webdriver',
70
70
  '\x1B[38;5;99mStarted\x1B[0m mac/webdriver/chrome',
71
71
  '\x1B[38;5;99mStarted\x1B[0m mac/webdriver/chrome/end-to-end',
72
- '\x1B[38;5;99mStarted\x1B[0m mac/webdriver/chrome/end-to-end/sharing2',
73
- '!Running: mac/webdriver/chrome/end-to-end/sharing2/t_shared_editing.js, path: tests/webdriver/end-to-end/sharing/t_shared_editing.js',
72
+ '\x1B[38;5;99mStarted\x1B[0m mac/webdriver/chrome/end-to-end/sharing-verify',
73
+ '!Running: mac/webdriver/chrome/end-to-end/sharing-verify/t_shared_editing.js, path: tests/webdriver/end-to-end/sharing/t_shared_editing.js',
74
74
  '\x1B[32mOk:\x1B[0m WDSuccessio',
75
- '>mac/webdriver/chrome/end-to-end/sharing2/t_shared_editing.js completed in',
76
- '\x1B[38;5;243mCompleted\x1B[0m mac/webdriver/chrome/end-to-end/sharing2',
75
+ '>mac/webdriver/chrome/end-to-end/sharing-verify/t_shared_editing.js completed in',
76
+ '\x1B[38;5;243mCompleted\x1B[0m mac/webdriver/chrome/end-to-end/sharing-verify',
77
77
  'Logs are written to',
78
78
  '\x1B[38;5;243mCompleted\x1B[0m mac/webdriver/chrome/end-to-end',
79
79
  'Logs are written to',
@@ -55,14 +55,14 @@ export async function test() {
55
55
  'Logs are written to',
56
56
  '\x1B[38;5;99mStarted\x1B[0m mac/',
57
57
  '\x1B[38;5;99mStarted\x1B[0m mac/unit',
58
- '\x1B[38;5;99mStarted\x1B[0m mac/unit/core2',
59
- '!Running: mac/unit/core2/t_presto.js, path: tests/unit/core/t_presto.js',
60
- '>mac/unit/core2/t_presto.js completed in',
61
- '\x1B[38;5;243mCompleted\x1B[0m mac/unit/core2',
58
+ '\x1B[38;5;99mStarted\x1B[0m mac/unit/core-verify',
59
+ '!Running: mac/unit/core-verify/t_presto.js, path: tests/unit/core/t_presto.js',
60
+ '>mac/unit/core-verify/t_presto.js completed in',
61
+ '\x1B[38;5;243mCompleted\x1B[0m mac/unit/core-verify',
62
62
  'Logs are written to',
63
63
  '\x1B[38;5;243mCompleted\x1B[0m mac/unit',
64
64
  'Logs are written to',
65
- '\x1B[41m\x1B[37m>mac/unit/core2/t_presto.js\x1B[0m Failure count: 1',
65
+ '\x1B[41m\x1B[37m>mac/unit/core-verify/t_presto.js\x1B[0m Failure count: 1',
66
66
  '\x1B[41m\x1B[37mFailed!\x1B[0m Passed: 0. Failed: 1',
67
67
  '\x1B[38;5;243mCompleted\x1B[0m mac/',
68
68
  'Logs are written to',
@@ -75,7 +75,7 @@ export async function test() {
75
75
  '\x1B[31mFailed:\x1B[0m Presto',
76
76
  '\x1B[31m>mac/unit/core/t_presto.js\x1B[0m has 1 failure(s)',
77
77
  '\x1B[31mFailed:\x1B[0m Presto',
78
- '\x1B[31m>mac/unit/core2/t_presto.js\x1B[0m has 1 failure(s)',
78
+ '\x1B[31m>mac/unit/core-verify/t_presto.js\x1B[0m has 1 failure(s)',
79
79
  ];
80
80
 
81
81
  await is_test_output(
@@ -30,7 +30,7 @@ function getChromeOptions() {
30
30
  height: settings.webdriver_window_height,
31
31
  });
32
32
  if (settings.webdriver_headless) {
33
- chromeOptions.addArguments('headless');
33
+ chromeOptions.addArguments('headless=new');
34
34
  }
35
35
  if (settings.webdriver == 'chrome-mobile') {
36
36
  chromeOptions.setMobileEmulation({
@@ -1,7 +1,7 @@
1
1
  import { By, Condition, Key, until } from 'selenium-webdriver';
2
2
  import { test_is, test_contains, is, contains, ok } from '../core/base.js';
3
3
  import { assert, fail } from '../core/core.js';
4
- import { is_mac, stringify, toDataURL } from '../core/util.js';
4
+ import { isMac, stringify, toDataURL } from '../core/util.js';
5
5
  import { log } from '../logging/logging.js';
6
6
  import { getTimeout, DriverBase } from './driver-base.js';
7
7
 
@@ -841,7 +841,7 @@ class Driver extends DriverBase {
841
841
  assert(msg, `selectAll: no msg`);
842
842
 
843
843
  if (this.firefox) {
844
- let accel = (is_mac() && Key.COMMAND) || Key.CONTROL;
844
+ let accel = (isMac() && Key.COMMAND) || Key.CONTROL;
845
845
  return this.waitForElementToInvoke(
846
846
  selector,
847
847
  el => el.sendKeys(Key.chord(accel, 'a')),
@@ -1,6 +1,7 @@
1
1
  import { assert, group, fail, info } from '../core/core.js';
2
2
  import { log, log_error } from '../logging/logging.js';
3
3
  import { Driver } from './driver.js';
4
+ import { isMac, isLinux, isWin } from '../core/util.js';
4
5
 
5
6
  let active_sessions = [];
6
7
 
@@ -113,6 +114,12 @@ async function start_session(arg1, arg2) {
113
114
 
114
115
  s.isSafari = () => s.driver.safari;
115
116
 
117
+ s.isMac = () => isMac();
118
+
119
+ s.isLinux = () => isLinux();
120
+
121
+ s.isWin = () => isWin();
122
+
116
123
  /**
117
124
  * Pause the session for number of secs.
118
125
  */