@camperaid/watest 2.3.4 → 2.3.8

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
@@ -18,6 +18,11 @@ const cfg = {
18
18
  */
19
19
  log_dir: process.env.WATEST_LOG_DIR,
20
20
 
21
+ /**
22
+ * If a test takes longer than the given number, the test will fail.
23
+ */
24
+ timeout: process.env.WATEST_TIMEOUT,
25
+
21
26
  /**
22
27
  * Temporary storage dir. Recreated each test run. Shall be used to store
23
28
  * files generated by tests if any.
package/core/base.js CHANGED
@@ -324,6 +324,17 @@ function is_object_impl(
324
324
  return false;
325
325
  }
326
326
 
327
+ // Date
328
+ if (got instanceof Date) {
329
+ if (got.valueOf() == expected.valueOf()) {
330
+ return true;
331
+ }
332
+ let got_str = stringify(got);
333
+ let expected_str = `expected: ${stringify(expected)}`;
334
+ fail_(`${contextmsg} unexpected value: ${got_str}, ${expected_str}`);
335
+ return false;
336
+ }
337
+
327
338
  // Convert Set and Map to Arrays.
328
339
  if (got instanceof Set) {
329
340
  got = Array.from(got.values());
@@ -537,9 +548,7 @@ function is_out(got, expected, msg) {
537
548
 
538
549
  let min = Math.min(got_i.length, exp_i.length);
539
550
  for (let j = 0; j < min; j++) {
540
- log_error(
541
- `${j}\t'${got_i[j]}'\t'${exp_i[j]}'\t${exp_i[j] == got_i[j]}`
542
- );
551
+ log_error(`${j}\t'${got_i[j]}'\t'${exp_i[j]}'\t${exp_i[j] == got_i[j]}`);
543
552
  }
544
553
 
545
554
  for (let j = min; j < got_i.length; j++) {
package/core/core.js CHANGED
@@ -57,11 +57,16 @@ class Core {
57
57
  }
58
58
 
59
59
  fail(msg) {
60
- const m = msg;
60
+ if (typeof msg == 'object') {
61
+ inspect(msg);
62
+ this.unconditional_fail('Unexpected exception');
63
+ return;
64
+ }
65
+
61
66
  for (let v of this.expectedFailures) {
62
67
  if (v.group == '*' || v.group == this.currgroup) {
63
68
  let f = v.list.find(f => f[1] == 0 || f[0] == '*');
64
- if (f && (f[0] == '*' || m.includes(f[0]))) {
69
+ if (f && (f[0] == '*' || msg.includes(f[0]))) {
65
70
  f[1]++;
66
71
  this.warn(msg);
67
72
 
@@ -78,11 +83,6 @@ class Core {
78
83
  }
79
84
  }
80
85
 
81
- if (typeof msg == 'object') {
82
- inspect(msg);
83
- msg = 'Unexpected exception';
84
- }
85
-
86
86
  this.unconditional_fail(msg);
87
87
  }
88
88
 
package/core/series.js CHANGED
@@ -637,7 +637,23 @@ class Series {
637
637
  let start_time = new Date();
638
638
  try {
639
639
  this.core.setExpectedFailures(failures_info);
640
- await func(); // execute the test
640
+
641
+ // If timeout is given then race it against the test.
642
+ if (settings.timeout) {
643
+ let kungFuDeathGripTimer = 0;
644
+ let kungFuDeathGrip = new Promise(r => {
645
+ kungFuDeathGripTimer = setTimeout(r, settings.timeout);
646
+ }).then(() =>
647
+ fail(
648
+ `Test ${name} takes longer than ${settings.timeout}ms. It's either slow or never ends.`
649
+ )
650
+ );
651
+
652
+ await Promise.race([func(), kungFuDeathGrip]);
653
+ clearTimeout(kungFuDeathGripTimer);
654
+ } else {
655
+ await func(); // execute the test
656
+ }
641
657
  } catch (e) {
642
658
  let failmsg = e;
643
659
  if (e instanceof Error) {
package/core/settings.js CHANGED
@@ -50,6 +50,10 @@ class Settings {
50
50
  return parseInt(rc.debunk_limit) || 5;
51
51
  }
52
52
 
53
+ get timeout() {
54
+ return parseInt(rc.timeout) || 0;
55
+ }
56
+
53
57
  setupTmpStorageDir() {
54
58
  if (!rc.tmp_dir) {
55
59
  console.log(`Settings: no temporary storage dir`);
package/core/util.js CHANGED
@@ -8,9 +8,7 @@ const cfg = require('./settings.js');
8
8
  * Logs object in console colored.
9
9
  */
10
10
  function inspect(obj) {
11
- log(
12
- require('util').inspect(obj, false, null, true /* enable colors */)
13
- );
11
+ log(require('util').inspect(obj, false, null, true /* enable colors */));
14
12
  }
15
13
 
16
14
  /**
@@ -31,6 +29,9 @@ function stringify(obj, traces = new Set()) {
31
29
  if (obj instanceof Array) {
32
30
  return `[${obj.map(i => stringify(i, traces)).join(', ')}]`;
33
31
  }
32
+ if (obj instanceof Date) {
33
+ return obj.toISOString();
34
+ }
34
35
  if (obj instanceof Set) {
35
36
  let values = Array.from(obj.values());
36
37
  return `Set[${values.map(i => stringify(i, traces)).join(', ')}]`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camperaid/watest",
3
- "version": "2.3.4",
3
+ "version": "2.3.8",
4
4
  "description": "Web Application Testsuite",
5
5
  "engines": {
6
6
  "node": ">=14.15.1"
@@ -27,6 +27,25 @@ module.exports.test = async () => {
27
27
  `types: Map`
28
28
  );
29
29
 
30
+ // types: Date success
31
+ await is_output(
32
+ () => is_object(new Date('2022-01-01'), new Date('2022-01-01'), 'TstMsg'),
33
+ [`Ok: TstMsg, got: 2022-01-01T00:00:00.000Z`],
34
+ [],
35
+ `types: Date sucess`
36
+ );
37
+
38
+ // types: Date failure
39
+ await is_output(
40
+ () => is_object(new Date('2022-01-01'), new Date('2023-02-02'), 'TstMsg'),
41
+ [],
42
+ [
43
+ `Failed: TstMsg: unexpected value: 2022-01-01T00:00:00.000Z, expected: 2023-02-02T00:00:00.000Z`,
44
+ `Failed: TstMsg`,
45
+ ],
46
+ `types: Date failure`
47
+ );
48
+
30
49
  // function sucess
31
50
  await is_output(
32
51
  () => is_object({ field: 'hey' }, () => true, 'TstMsg'),
@@ -21,6 +21,7 @@ module.exports.test = () => {
21
21
  is(stringify(new Set(['v1', 'v2'])), `Set['v1', 'v2']`, 'set');
22
22
  is(stringify(new Map([['key', 'value']])), `Map{key: 'value'}`, 'map');
23
23
  is(stringify(/\d+/), `/\\d+/`, 'regexp');
24
+ is(stringify(new Date('2022-01-01')), '2022-01-01T00:00:00.000Z', `Date`);
24
25
 
25
26
  // functions
26
27
  is(
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.3.4",
19
+ "version": "2.3.8",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.3.4",
19
+ "version": "2.3.8",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.3.4",
19
+ "version": "2.3.8",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.3.4",
19
+ "version": "2.3.8",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "../../../..": {
15
15
  "name": "@camperaid/watest",
16
- "version": "2.3.4",
16
+ "version": "2.3.8",
17
17
  "dev": true,
18
18
  "license": "MPL",
19
19
  "dependencies": {
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.3.4",
19
+ "version": "2.3.8",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -6,7 +6,7 @@
6
6
  "packages": {
7
7
  "../../../..": {
8
8
  "name": "@camperaid/watest",
9
- "version": "2.3.4",
9
+ "version": "2.3.8",
10
10
  "dev": true,
11
11
  "license": "MPL",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../../..": {
18
18
  "name": "@camperaid/watest",
19
- "version": "2.3.4",
19
+ "version": "2.3.8",
20
20
  "dev": true,
21
21
  "license": "MPL",
22
22
  "dependencies": {
@@ -25,7 +25,7 @@ module.exports.test = do_self_tests(snippet, async session => {
25
25
  `Test: Input is shown. Selector: '#input'`,
26
26
  `Ok: '#input' has to be unique, got: 1`,
27
27
  `Ok: Input is shown`,
28
- `Action: Input.type into Type hello`,
28
+ `Action: Input.type 'hello' into Type hello`,
29
29
  `Test: Focus. Selector: '#input'`,
30
30
  `Ok: '#input' has to be unique, got: 1`,
31
31
  `Ok: Focus`,
@@ -47,7 +47,7 @@ module.exports.test = do_self_tests(snippet, async session => {
47
47
  `Test: Input is shown. Selector: '#input'`,
48
48
  `Ok: '#input' has to be unique, got: 1`,
49
49
  `Ok: Input is shown`,
50
- `Action: Input.type into Type hello`,
50
+ `Action: Input.type 'hello' into Type hello`,
51
51
  `Test: Focus. Selector: '#input'`,
52
52
  `Ok: '#input' has to be unique, got: 1`,
53
53
  `Ok: Focus`,
@@ -73,7 +73,7 @@ class AppDriver {
73
73
 
74
74
  type(selector, value, field) {
75
75
  return this.chain(() =>
76
- this.action(`${this.uiname}.type into ${field}`)
76
+ this.action(`${this.uiname}.type '${value}' into ${field}`)
77
77
  .sendKeys(selector, '', `Focus`)
78
78
  .elementFocused(selector, 'Focused')
79
79
  .selectAll(selector, `Select all text`)
@@ -82,6 +82,14 @@ class AppDriver {
82
82
  );
83
83
  }
84
84
 
85
+ clear(selector, field) {
86
+ return this.chain(() =>
87
+ this.action(`${this.uiname}.clear ${field}`)
88
+ .clear(selector, `Clear text`)
89
+ .textIs(selector, '', `Check ${field} text`)
90
+ );
91
+ }
92
+
85
93
  setValue(selector, value, field) {
86
94
  return this.chain(() =>
87
95
  this.action(`${this.uiname}.setValue for ${field}`)
@@ -100,7 +108,7 @@ class AppDriver {
100
108
 
101
109
  uncheck(selector, field) {
102
110
  return this.chain(() =>
103
- this.action(`${this.constructor.name}.uncheck ${field}`)
111
+ this.action(`${this.uiname}.uncheck ${field}`)
104
112
  .click(selector, `Click at ${field}`)
105
113
  .hasElements(
106
114
  `${selector}:not(:checked)`,
@@ -109,10 +117,19 @@ class AppDriver {
109
117
  );
110
118
  }
111
119
 
120
+ execute(script, descr) {
121
+ return this.chain(() =>
122
+ this.action(`${this.uiname}.execute ${descr}`)
123
+ .executeScript(script, `Execute script`)
124
+ );
125
+ }
126
+
112
127
  // Returns a name for the tested class. Typically the name convension for
113
128
  // UI drivers is a test class name postixed by Driver.
114
129
  get uiname() {
115
- return this.constructor.name.replace(/Driver$/, '');
130
+ let o = this;
131
+ while ((o = Object.getPrototypeOf(o)) && o.constructor.name == '');
132
+ return o.constructor.name.replace(/Driver$/, '');
116
133
  }
117
134
 
118
135
  defineSelectors(context, selectors) {
@@ -169,13 +169,47 @@ class Driver extends DriverBase {
169
169
  );
170
170
  }
171
171
 
172
+ /**
173
+ * Waits untils an element defined by a selector has attribute.
174
+ */
175
+ hasAttribute(selector, attr, msg) {
176
+ assert(selector, `hasAttribute: no selector`);
177
+ assert(attr, `hasAttribute: no attr`);
178
+ assert(msg, `hasAttribute: no msg`);
179
+
180
+ return this.matchAttribute({
181
+ selector,
182
+ attr,
183
+ msg,
184
+ test: got => got !== null,
185
+ expected_stringified: stringify(null),
186
+ });
187
+ }
188
+
189
+ /**
190
+ * Waits untils an element defined by a selector has no attribute.
191
+ */
192
+ noAttribute(selector, attr, msg) {
193
+ assert(selector, `noAttribute: no selector`);
194
+ assert(attr, `noAttribute: no attr`);
195
+ assert(msg, `noAttribute: no msg`);
196
+
197
+ return this.matchAttribute({
198
+ selector,
199
+ attr,
200
+ msg,
201
+ test: got => got === null,
202
+ expected_stringified: stringify(null),
203
+ });
204
+ }
205
+
172
206
  /**
173
207
  * Waits untils an element defined by a selector has attribute of a given value.
174
208
  */
175
209
  attributeIs(selector, attr, value, msg) {
176
210
  assert(selector, `attributeIs: no selector`);
177
211
  assert(attr, `attributeIs: no attr`);
178
- assert(value != undefined, `attributeIs: no value`);
212
+ assert(value !== undefined, `attributeIs: no value`);
179
213
  assert(msg, `attributeIs: no msg`);
180
214
 
181
215
  return this.matchAttribute({
@@ -187,6 +221,29 @@ class Driver extends DriverBase {
187
221
  });
188
222
  }
189
223
 
224
+ /**
225
+ * Waits untils an element defined by a selector has attribute of a given value.
226
+ */
227
+ attributeIsAll(selector, attr, values, msg) {
228
+ assert(selector, `attributeIsAll: no selector`);
229
+ assert(attr, `attributeIsAll: no attr`);
230
+ assert(values, `attributeIsAll: no values`);
231
+ assert(msg, `attributeIsAll: no msg`);
232
+
233
+ return this.matchAttributeAll({
234
+ selector,
235
+ attr,
236
+ values,
237
+ msg,
238
+ test: got =>
239
+ test_is(
240
+ got,
241
+ values.map(value => got => got == value)
242
+ ),
243
+ expected_stringified: stringify(values),
244
+ });
245
+ }
246
+
190
247
  /**
191
248
  * Waits untils an element defined by a selector has attribute of a given value.
192
249
  */
@@ -800,10 +857,14 @@ else {
800
857
  window.getSelection().addRange(r);
801
858
  }
802
859
  `;
803
- return this.run(() => this.dvr.executeScript(script).catch(e => {
804
- fail(`Failed to execute selectAll script: ${script}`);
805
- throw e;
806
- }), msg);
860
+ return this.run(
861
+ () =>
862
+ this.dvr.executeScript(script).catch(e => {
863
+ fail(`Failed to execute selectAll script: ${script}`);
864
+ throw e;
865
+ }),
866
+ msg
867
+ );
807
868
  }
808
869
 
809
870
  /**