@testspectra/matchers 1.0.70 → 1.1.0-rc.0

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 (51) hide show
  1. package/LICENSE.md +48 -0
  2. package/dist/__tests__/intercept.test.d.ts +1 -0
  3. package/dist/__tests__/intercept.test.js +183 -0
  4. package/dist/__tests__/matcher-contracts.test.d.ts +1 -0
  5. package/dist/__tests__/matcher-contracts.test.js +498 -0
  6. package/dist/__tests__/matchers.test.d.ts +1 -0
  7. package/dist/__tests__/matchers.test.js +161 -0
  8. package/dist/contract.d.ts +141 -0
  9. package/dist/contract.js +10 -0
  10. package/dist/index.d.ts +4 -21
  11. package/dist/index.js +4 -21
  12. package/dist/intercept/cdp-handler.d.ts +22 -0
  13. package/dist/intercept/cdp-handler.js +123 -0
  14. package/dist/intercept/index.d.ts +4 -0
  15. package/dist/intercept/index.js +4 -0
  16. package/dist/intercept/mock-handle.d.ts +49 -0
  17. package/dist/intercept/mock-handle.js +124 -0
  18. package/dist/intercept/mock-registry.d.ts +18 -0
  19. package/dist/intercept/mock-registry.js +97 -0
  20. package/dist/intercept/types.d.ts +43 -0
  21. package/dist/intercept/types.js +1 -0
  22. package/dist/matchers.d.ts +15 -4
  23. package/dist/matchers.js +609 -81
  24. package/dist/proto.d.ts +18 -283
  25. package/dist/proto.js +1 -114
  26. package/dist/reporter.d.ts +30 -0
  27. package/dist/reporter.js +97 -0
  28. package/dist/runner/collection.d.ts +33 -20
  29. package/dist/runner/collection.js +104 -26
  30. package/dist/runner/single.d.ts +125 -34
  31. package/dist/runner/single.js +290 -55
  32. package/dist/semantic.d.ts +11 -0
  33. package/dist/semantic.js +141 -0
  34. package/dist/spectra.d.ts +32 -8
  35. package/dist/spectra.js +157 -40
  36. package/dist/types.d.ts +1226 -28
  37. package/package.json +13 -8
  38. package/src/contract.ts +182 -0
  39. package/src/index.ts +4 -22
  40. package/src/proto.ts +22 -433
  41. package/src/runtime/assertions.ts +450 -0
  42. package/src/runtime/element_actions.ts +169 -0
  43. package/src/runtime/element_proxy.ts +159 -0
  44. package/src/runtime/element_state.ts +91 -0
  45. package/src/runtime/spectra.ts +109 -0
  46. package/src/types.ts +1424 -96
  47. package/tsconfig.json +2 -2
  48. package/src/matchers.ts +0 -146
  49. package/src/runner/collection.ts +0 -153
  50. package/src/runner/single.ts +0 -301
  51. package/src/spectra.ts +0 -376
@@ -1,31 +1,46 @@
1
- import { executeSingleMatcher, resolveElement } from "../matchers.js";
1
+ import { executeSingleMatcher, resolveElement } from '../matchers.js';
2
+ import { trackCommand } from '../reporter.js';
3
+ function getRawTarget(target) {
4
+ if (!target)
5
+ return '';
6
+ if (typeof target === 'string')
7
+ return target;
8
+ if (target.selector)
9
+ return target.selector;
10
+ if (target.constructor && target.constructor.name && target.constructor.name !== 'Object')
11
+ return target.constructor.name;
12
+ return String(target);
13
+ }
2
14
  /**
3
15
  * Fluent interaction and assertion runner for a single element or browser context.
4
16
  *
5
- * Implements `PromiseLike<void>`, allowing it to be awaited directly or chained with
6
- * multiple actions and `.should(...)` assertions.
17
+ * Implements `PromiseLike<void>` and `ElementReceiverAssertions<SingleElementRunner>`,
18
+ * allowing it to be awaited directly or chained with multiple actions and direct `.should...` assertions.
7
19
  *
8
20
  * @example
9
21
  * ```ts
10
22
  * // Direct action + chained assertion
11
23
  * await Spectra.get("#submit-btn")
12
24
  * .click()
13
- * .should("not.be.visible");
25
+ * .shouldNotBeVisible();
14
26
  *
15
27
  * // Query + multiple assertions
16
28
  * await Spectra.get(LoginPage.usernameInput)
17
- * .should("be.visible")
18
- * .should("be.enabled")
19
- * .should("have.value", "admin");
29
+ * .shouldBeVisible()
30
+ * .shouldBeEnabled()
31
+ * .shouldHaveValue("admin");
20
32
  * ```
21
33
  */
22
34
  export class SingleElementRunner {
23
35
  _target;
24
36
  _action = null;
37
+ _actionPayload = null;
25
38
  _assertions = [];
26
- constructor(target, action) {
39
+ constructor(target, action, actionPayload) {
27
40
  this._target = target;
28
41
  this._action = action || null;
42
+ this._actionPayload =
43
+ actionPayload || (action ? { type: 'action', key: 'action', target: getRawTarget(target), args: [] } : null);
29
44
  }
30
45
  // --- Chained Actions on current target ---
31
46
  /**
@@ -38,7 +53,8 @@ export class SingleElementRunner {
38
53
  * @returns Current runner instance for method chaining.
39
54
  */
40
55
  click() {
41
- const target = this.requireTarget("click");
56
+ const target = this.requireTarget('click');
57
+ this._actionPayload = { type: 'action', key: 'click', target: getRawTarget(target), args: [] };
42
58
  this._action = async () => {
43
59
  const el = await resolveElement(target);
44
60
  await el.click();
@@ -55,7 +71,8 @@ export class SingleElementRunner {
55
71
  * @returns Current runner instance for method chaining.
56
72
  */
57
73
  doubleClick() {
58
- const target = this.requireTarget("doubleClick");
74
+ const target = this.requireTarget('doubleClick');
75
+ this._actionPayload = { type: 'action', key: 'doubleClick', target: getRawTarget(target), args: [] };
59
76
  this._action = async () => {
60
77
  const el = await resolveElement(target);
61
78
  await el.doubleClick();
@@ -73,14 +90,12 @@ export class SingleElementRunner {
73
90
  * @returns Current runner instance for method chaining.
74
91
  */
75
92
  longPress(durationMs = 1000) {
76
- const target = this.requireTarget("longPress");
93
+ const target = this.requireTarget('longPress');
94
+ this._actionPayload = { type: 'action', key: 'longPress', target: getRawTarget(target), args: [durationMs] };
77
95
  this._action = async () => {
78
96
  const el = await resolveElement(target);
79
- if (typeof el.touchAction === "function") {
80
- await el.touchAction([
81
- { action: "longPress", ms: durationMs },
82
- { action: "release" },
83
- ]);
97
+ if (typeof el.touchAction === 'function') {
98
+ await el.touchAction([{ action: 'longPress', ms: durationMs }, { action: 'release' }]);
84
99
  }
85
100
  else {
86
101
  await el.click();
@@ -100,13 +115,22 @@ export class SingleElementRunner {
100
115
  * @returns Current runner instance for method chaining.
101
116
  */
102
117
  type(value, options) {
103
- const target = this.requireTarget("type");
118
+ const target = this.requireTarget('type');
119
+ this._actionPayload = { type: 'action', key: 'type', target: getRawTarget(target), args: [value] };
104
120
  this._action = async () => {
105
121
  const el = await resolveElement(target);
106
122
  if (options?.clearFirst) {
107
- await el.clearValue();
123
+ if (typeof el.clearValue === 'function')
124
+ await el.clearValue();
125
+ else if (typeof el.clear === 'function')
126
+ await el.clear();
108
127
  }
109
- await el.setValue(value);
128
+ if (typeof el.setValue === 'function')
129
+ await el.setValue(value);
130
+ else if (typeof el.fill === 'function')
131
+ await el.fill(value);
132
+ else if (typeof el.type === 'function')
133
+ await el.type(value);
110
134
  };
111
135
  return this;
112
136
  }
@@ -120,10 +144,14 @@ export class SingleElementRunner {
120
144
  * @returns Current runner instance for method chaining.
121
145
  */
122
146
  clear() {
123
- const target = this.requireTarget("clear");
147
+ const target = this.requireTarget('clear');
148
+ this._actionPayload = { type: 'action', key: 'clear', target: getRawTarget(target), args: [] };
124
149
  this._action = async () => {
125
150
  const el = await resolveElement(target);
126
- await el.clearValue();
151
+ if (typeof el.clearValue === 'function')
152
+ await el.clearValue();
153
+ else if (typeof el.clear === 'function')
154
+ await el.clear();
127
155
  };
128
156
  return this;
129
157
  }
@@ -138,27 +166,35 @@ export class SingleElementRunner {
138
166
  * @returns Current runner instance for method chaining.
139
167
  */
140
168
  select(value) {
141
- const target = this.requireTarget("select");
169
+ const target = this.requireTarget('select');
170
+ this._actionPayload = { type: 'action', key: 'selectOption', target: getRawTarget(target), args: [value] };
142
171
  this._action = async () => {
143
172
  const el = await resolveElement(target);
144
- await el.selectByVisibleText(value);
173
+ if (typeof el.selectByVisibleText === 'function')
174
+ await el.selectByVisibleText(value);
175
+ else if (typeof el.selectOption === 'function')
176
+ await el.selectOption({ label: value });
145
177
  };
146
178
  return this;
147
179
  }
148
180
  /**
149
- * Moves the mouse cursor over the target element (hover).
181
+ * Hovers over the target element.
150
182
  *
151
183
  * @example
152
184
  * ```ts
153
- * await Spectra.get("#dropdown-menu-trigger").hover();
185
+ * await Spectra.get("#dropdown-trigger").hover();
154
186
  * ```
155
187
  * @returns Current runner instance for method chaining.
156
188
  */
157
189
  hover() {
158
- const target = this.requireTarget("hover");
190
+ const target = this.requireTarget('hover');
191
+ this._actionPayload = { type: 'action', key: 'hover', target: getRawTarget(target), args: [] };
159
192
  this._action = async () => {
160
193
  const el = await resolveElement(target);
161
- await el.moveTo();
194
+ if (typeof el.moveTo === 'function')
195
+ await el.moveTo();
196
+ else if (typeof el.hover === 'function')
197
+ await el.hover();
162
198
  };
163
199
  return this;
164
200
  }
@@ -173,40 +209,223 @@ export class SingleElementRunner {
173
209
  * @returns Current runner instance for method chaining.
174
210
  */
175
211
  waitForElement(timeoutMs = 10000) {
176
- const target = this.requireTarget("waitForElement");
212
+ const target = this.requireTarget('waitForElement');
213
+ this._actionPayload = { type: 'action', key: 'waitForElement', target: getRawTarget(target), args: [timeoutMs] };
177
214
  this._action = async () => {
178
215
  const el = await resolveElement(target);
179
216
  await el.waitForDisplayed({ timeout: timeoutMs });
180
217
  };
181
218
  return this;
182
219
  }
183
- should(a, b, ...rest) {
184
- const isFirstArgMatcher = typeof a === "string" &&
185
- (a.includes(".") ||
186
- a.includes("exist") ||
187
- a.includes("visible") ||
188
- a.includes("enabled") ||
189
- a.includes("disabled") ||
190
- a.includes("selected") ||
191
- a.includes("checked"));
192
- if (b !== undefined && !isFirstArgMatcher) {
193
- // Form: should(target, matcher, ...args)
194
- this._assertions.push({
195
- target: a,
196
- matcher: b,
197
- args: rest,
198
- });
199
- }
200
- else {
201
- // Form: should(matcher, ...args) -> on current target
202
- this._assertions.push({
203
- target: this._target,
204
- matcher: a,
205
- args: b !== undefined ? [b, ...rest] : rest,
206
- });
207
- }
220
+ /**
221
+ * Performs a right-click (context menu) interaction on the target element.
222
+ */
223
+ rightClick() {
224
+ const target = this.requireTarget('rightClick');
225
+ this._actionPayload = { type: 'action', key: 'rightClick', target: getRawTarget(target), args: [] };
226
+ this._action = async () => {
227
+ const el = await resolveElement(target);
228
+ await el.click({ button: 'right' });
229
+ };
230
+ return this;
231
+ }
232
+ /**
233
+ * Drags the target element and drops it onto the specified destination element.
234
+ */
235
+ dragDrop(destination) {
236
+ const target = this.requireTarget('dragDrop');
237
+ this._actionPayload = {
238
+ type: 'action',
239
+ key: 'dragDrop',
240
+ target: getRawTarget(target),
241
+ args: [getRawTarget(destination)],
242
+ };
243
+ this._action = async () => {
244
+ const sourceEl = await resolveElement(target);
245
+ const destEl = await resolveElement(destination);
246
+ await sourceEl.dragAndDrop(destEl);
247
+ };
248
+ return this;
249
+ }
250
+ /**
251
+ * Scrolls the target element into view.
252
+ */
253
+ scrollIntoView() {
254
+ const target = this.requireTarget('scrollIntoView');
255
+ this._actionPayload = { type: 'action', key: 'scrollIntoView', target: getRawTarget(target), args: [] };
256
+ this._action = async () => {
257
+ const el = await resolveElement(target);
258
+ await el.scrollIntoView();
259
+ };
260
+ return this;
261
+ }
262
+ _pushAssertion(matcher, args = []) {
263
+ this._assertions.push({
264
+ target: this._target,
265
+ matcher,
266
+ args,
267
+ });
208
268
  return this;
209
269
  }
270
+ // --- Fluent Assertion Matchers ---
271
+ /**
272
+ * Asserts that the target element is displayed and visible to the user.
273
+ */
274
+ shouldBeVisible() {
275
+ return this._pushAssertion('be.visible');
276
+ }
277
+ /**
278
+ * Asserts that the target element is not displayed or hidden from view.
279
+ */
280
+ shouldNotBeVisible() {
281
+ return this._pushAssertion('not.be.visible');
282
+ }
283
+ /**
284
+ * Asserts that the target element exists in the DOM.
285
+ */
286
+ shouldExist() {
287
+ return this._pushAssertion('exist');
288
+ }
289
+ /**
290
+ * Asserts that the target element does not exist in the DOM.
291
+ */
292
+ shouldNotExist() {
293
+ return this._pushAssertion('not.exist');
294
+ }
295
+ /**
296
+ * Asserts that the target element is clickable.
297
+ */
298
+ shouldBeClickable() {
299
+ return this._pushAssertion('be.clickable');
300
+ }
301
+ /**
302
+ * Asserts that the target element is not clickable.
303
+ */
304
+ shouldNotBeClickable() {
305
+ return this._pushAssertion('not.be.clickable');
306
+ }
307
+ /**
308
+ * Asserts that the target element is enabled (not disabled).
309
+ */
310
+ shouldBeEnabled() {
311
+ return this._pushAssertion('be.enabled');
312
+ }
313
+ /**
314
+ * Asserts that the target element is disabled.
315
+ */
316
+ shouldBeDisabled() {
317
+ return this._pushAssertion('be.disabled');
318
+ }
319
+ /**
320
+ * Asserts that the target checkbox or radio element is checked.
321
+ */
322
+ shouldBeChecked() {
323
+ return this._pushAssertion('be.checked');
324
+ }
325
+ /**
326
+ * Asserts that the target checkbox or radio element is not checked.
327
+ */
328
+ shouldNotBeChecked() {
329
+ return this._pushAssertion('not.be.checked');
330
+ }
331
+ /**
332
+ * Asserts that the target element currently has focus.
333
+ */
334
+ shouldBeFocused() {
335
+ return this._pushAssertion('be.focused');
336
+ }
337
+ /**
338
+ * Asserts that the target element does not have focus.
339
+ */
340
+ shouldNotBeFocused() {
341
+ return this._pushAssertion('not.be.focused');
342
+ }
343
+ /**
344
+ * Asserts that the target element has exact inner text matching the expected value.
345
+ */
346
+ shouldHaveText(expected) {
347
+ return this._pushAssertion('have.text', [expected]);
348
+ }
349
+ /**
350
+ * Asserts that the target element does not have inner text matching the expected value.
351
+ */
352
+ shouldNotHaveText(expected) {
353
+ return this._pushAssertion('not.have.text', [expected]);
354
+ }
355
+ /**
356
+ * Asserts that the target element's text contains the given substring.
357
+ */
358
+ shouldContainText(substring) {
359
+ return this._pushAssertion('contain.text', [substring]);
360
+ }
361
+ /**
362
+ * Asserts that the target element's text does not contain the given substring.
363
+ */
364
+ shouldNotContainText(substring) {
365
+ return this._pushAssertion('not.contain.text', [substring]);
366
+ }
367
+ /**
368
+ * Asserts that the target input element has the exact expected value.
369
+ */
370
+ shouldHaveValue(value) {
371
+ return this._pushAssertion('have.value', [value]);
372
+ }
373
+ /**
374
+ * Asserts that the target input element does not have the expected value.
375
+ */
376
+ shouldNotHaveValue(value) {
377
+ return this._pushAssertion('not.have.value', [value]);
378
+ }
379
+ /**
380
+ * Asserts that the target input element's value contains the given substring.
381
+ */
382
+ shouldContainValue(substring) {
383
+ return this._pushAssertion('contain.value', [substring]);
384
+ }
385
+ /**
386
+ * Asserts that the target input element's value does not contain the given substring.
387
+ */
388
+ shouldNotContainValue(substring) {
389
+ return this._pushAssertion('not.contain.value', [substring]);
390
+ }
391
+ /**
392
+ * Asserts that the target element has the specified HTML attribute and optional value.
393
+ */
394
+ shouldHaveAttribute(name, value) {
395
+ return value !== undefined
396
+ ? this._pushAssertion('have.attr', [name, value])
397
+ : this._pushAssertion('have.attr', [name]);
398
+ }
399
+ /**
400
+ * Asserts that the target element does not have the specified HTML attribute.
401
+ */
402
+ shouldNotHaveAttribute(name) {
403
+ return this._pushAssertion('not.have.attr', [name]);
404
+ }
405
+ /**
406
+ * Asserts that the target element has the specified CSS class.
407
+ */
408
+ shouldHaveClass(className) {
409
+ return this._pushAssertion('have.class', [className]);
410
+ }
411
+ /**
412
+ * Asserts that the target element does not have the specified CSS class.
413
+ */
414
+ shouldNotHaveClass(className) {
415
+ return this._pushAssertion('not.have.class', [className]);
416
+ }
417
+ /**
418
+ * Asserts that the target element has the specified computed CSS property value.
419
+ */
420
+ shouldHaveCss(property, value) {
421
+ return this._pushAssertion('have.css', [property, value]);
422
+ }
423
+ /**
424
+ * Asserts that the target element does not have the specified computed CSS property value.
425
+ */
426
+ shouldNotHaveCss(property, value) {
427
+ return this._pushAssertion('not.have.css', [property, value]);
428
+ }
210
429
  // --- Execution when awaited ---
211
430
  /**
212
431
  * Standard Promise `then` implementation.
@@ -215,10 +434,26 @@ export class SingleElementRunner {
215
434
  async then(onfulfilled, onrejected) {
216
435
  try {
217
436
  if (this._action) {
218
- await this._action();
437
+ const payload = this._actionPayload || {
438
+ type: 'action',
439
+ key: 'action',
440
+ target: getRawTarget(this._target),
441
+ args: [],
442
+ };
443
+ await trackCommand('action', payload, async () => {
444
+ await this._action();
445
+ });
219
446
  }
220
447
  for (const assert of this._assertions) {
221
- await executeSingleMatcher(assert.target, assert.matcher, assert.args);
448
+ const payload = {
449
+ type: 'assertion',
450
+ key: assert.matcher,
451
+ target: getRawTarget(assert.target),
452
+ args: assert.args,
453
+ };
454
+ await trackCommand('assertion', payload, async () => {
455
+ await executeSingleMatcher(assert.target, assert.matcher, assert.args);
456
+ });
222
457
  }
223
458
  const res = (onfulfilled ? await onfulfilled() : undefined);
224
459
  return res;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # @testspectra/matchers - Semantic Natural Language Formatter
4
+ *
5
+ * Formats commands, actions, and assertions into clean, grammatically
6
+ * correct English sentences for real-time reporting.
7
+ */
8
+ export declare function formatSemanticTarget(target: any, isCollection?: boolean): string;
9
+ export declare function formatSemanticAction(action: string, target: any, args?: any[]): string;
10
+ export declare function formatSemanticMatcher(target: any, matcher: string, args?: any[], isCollection?: boolean): string;
11
+ export declare function formatSemanticBrowser(method: string, args?: any[]): string;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # @testspectra/matchers - Semantic Natural Language Formatter
4
+ *
5
+ * Formats commands, actions, and assertions into clean, grammatically
6
+ * correct English sentences for real-time reporting.
7
+ */
8
+ export function formatSemanticTarget(target, isCollection = false) {
9
+ if (target === undefined || target === null)
10
+ return isCollection ? 'collection' : 'element';
11
+ if (typeof target === 'string') {
12
+ return isCollection ? `collection "${target}"` : `element "${target}"`;
13
+ }
14
+ if (typeof target === 'object') {
15
+ if (target.selector) {
16
+ return isCollection ? `collection "${target.selector}"` : `element "${target.selector}"`;
17
+ }
18
+ if (target.constructor && target.constructor.name && target.constructor.name !== 'Object') {
19
+ return `element "${target.constructor.name}"`;
20
+ }
21
+ }
22
+ return `element "${String(target)}"`;
23
+ }
24
+ export function formatSemanticAction(action, target, args = []) {
25
+ const targetStr = formatSemanticTarget(target, false);
26
+ const arg0 = args[0] !== undefined ? String(args[0]) : '';
27
+ switch (action) {
28
+ case 'type':
29
+ return `Type "${arg0}" into ${targetStr}`;
30
+ case 'click':
31
+ return `Click on ${targetStr}`;
32
+ case 'doubleClick':
33
+ return `Double-click on ${targetStr}`;
34
+ case 'rightClick':
35
+ return `Right-click on ${targetStr}`;
36
+ case 'check':
37
+ return `Check checkbox ${targetStr}`;
38
+ case 'uncheck':
39
+ return `Uncheck checkbox ${targetStr}`;
40
+ case 'selectOption':
41
+ return `Select option "${arg0}" in ${targetStr}`;
42
+ case 'clear':
43
+ return `Clear text in ${targetStr}`;
44
+ case 'hover':
45
+ return `Hover over ${targetStr}`;
46
+ case 'scrollIntoView':
47
+ return `Scroll ${targetStr} into view`;
48
+ case 'navigate':
49
+ return `Navigate to "${arg0}"`;
50
+ case 'wait':
51
+ return `Wait for ${arg0}ms`;
52
+ case 'pause':
53
+ return `Pause execution for ${arg0}ms`;
54
+ default:
55
+ return `Perform ${action} on ${targetStr}`;
56
+ }
57
+ }
58
+ export function formatSemanticMatcher(target, matcher, args = [], isCollection = false) {
59
+ const targetStr = formatSemanticTarget(target, isCollection);
60
+ const arg0 = args[0] !== undefined ? String(args[0]) : '';
61
+ const arg1 = args[1] !== undefined ? String(args[1]) : '';
62
+ switch (matcher) {
63
+ case 'be.visible':
64
+ return `Expect ${targetStr} to be visible`;
65
+ case 'not.be.visible':
66
+ return `Expect ${targetStr} not to be visible`;
67
+ case 'exist':
68
+ return `Expect ${targetStr} to exist in DOM`;
69
+ case 'not.exist':
70
+ return `Expect ${targetStr} not to exist in DOM`;
71
+ case 'be.enabled':
72
+ return `Expect ${targetStr} to be enabled`;
73
+ case 'be.disabled':
74
+ return `Expect ${targetStr} to be disabled`;
75
+ case 'be.checked':
76
+ return `Expect ${targetStr} to be checked`;
77
+ case 'not.be.checked':
78
+ return `Expect ${targetStr} not to be checked`;
79
+ case 'have.text':
80
+ return `Expect ${targetStr} to have text "${arg0}"`;
81
+ case 'not.have.text':
82
+ return `Expect ${targetStr} not to have text "${arg0}"`;
83
+ case 'contain.text':
84
+ return `Expect ${targetStr} to contain text "${arg0}"`;
85
+ case 'not.contain.text':
86
+ return `Expect ${targetStr} not to contain text "${arg0}"`;
87
+ case 'have.value':
88
+ return `Expect ${targetStr} to have value "${arg0}"`;
89
+ case 'not.have.value':
90
+ return `Expect ${targetStr} not to have value "${arg0}"`;
91
+ case 'contain.value':
92
+ return `Expect ${targetStr} to contain value "${arg0}"`;
93
+ case 'not.contain.value':
94
+ return `Expect ${targetStr} not to contain value "${arg0}"`;
95
+ case 'have.class':
96
+ return `Expect ${targetStr} to have class "${arg0}"`;
97
+ case 'not.have.class':
98
+ return `Expect ${targetStr} not to have class "${arg0}"`;
99
+ case 'have.attr':
100
+ return args.length >= 2
101
+ ? `Expect ${targetStr} to have attribute "${arg0}" = "${arg1}"`
102
+ : `Expect ${targetStr} to have attribute "${arg0}"`;
103
+ case 'not.have.attr':
104
+ return `Expect ${targetStr} not to have attribute "${arg0}"`;
105
+ case 'be.focused':
106
+ return `Expect ${targetStr} to be focused`;
107
+ case 'be.selected':
108
+ return `Expect ${targetStr} to be selected`;
109
+ case 'be.empty':
110
+ return `Expect ${targetStr} to be empty`;
111
+ case 'not.be.empty':
112
+ return `Expect ${targetStr} not to be empty`;
113
+ case 'have.length':
114
+ return `Expect ${targetStr} count to equal ${arg0}`;
115
+ case 'have.length.greaterThan':
116
+ return `Expect ${targetStr} count to be greater than ${arg0}`;
117
+ case 'have.length.lessThan':
118
+ return `Expect ${targetStr} count to be less than ${arg0}`;
119
+ case 'have.length.atLeast':
120
+ return `Expect ${targetStr} count to be at least ${arg0}`;
121
+ case 'have.length.atMost':
122
+ return `Expect ${targetStr} count to be at most ${arg0}`;
123
+ default:
124
+ return `Expect ${targetStr} to match ${matcher}`;
125
+ }
126
+ }
127
+ export function formatSemanticBrowser(method, args = []) {
128
+ const arg0 = args[0] !== undefined ? String(args[0]) : '';
129
+ switch (method) {
130
+ case 'shouldContainTitle':
131
+ return `Expect browser title to contain "${arg0}"`;
132
+ case 'shouldHaveTitle':
133
+ return `Expect browser title to be "${arg0}"`;
134
+ case 'shouldContainUrl':
135
+ return `Expect browser URL to contain "${arg0}"`;
136
+ case 'shouldHaveUrl':
137
+ return `Expect browser URL to be "${arg0}"`;
138
+ default:
139
+ return `Expect browser to ${method} "${arg0}"`;
140
+ }
141
+ }