@sudobility/testomniac_runner_service 0.1.65 → 0.1.66
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/dist/analyzer/page-analyzer/generators/content.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/content.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/content.js +8 -8
- package/dist/analyzer/page-analyzer/generators/content.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/dialogs.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/dialogs.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/dialogs.js +6 -6
- package/dist/analyzer/page-analyzer/generators/dialogs.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/e2e.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/e2e.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/e2e.js +5 -5
- package/dist/analyzer/page-analyzer/generators/e2e.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/forms.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/forms.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/forms.js +21 -21
- package/dist/analyzer/page-analyzer/generators/forms.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.d.ts +2 -2
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.js +21 -21
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js +5 -5
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/navigation.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/navigation.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/navigation.js +6 -6
- package/dist/analyzer/page-analyzer/generators/navigation.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/render.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/render.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/render.js +6 -6
- package/dist/analyzer/page-analyzer/generators/render.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/scaffolds.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/scaffolds.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/scaffolds.js +8 -8
- package/dist/analyzer/page-analyzer/generators/scaffolds.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.js +5 -5
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.js.map +1 -1
- package/dist/analyzer/page-analyzer/generators/variants.d.ts +1 -1
- package/dist/analyzer/page-analyzer/generators/variants.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/generators/variants.js +5 -5
- package/dist/analyzer/page-analyzer/generators/variants.js.map +1 -1
- package/dist/analyzer/page-analyzer/index.d.ts +28 -28
- package/dist/analyzer/page-analyzer/index.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/index.js +100 -94
- package/dist/analyzer/page-analyzer/index.js.map +1 -1
- package/dist/analyzer/page-analyzer/types.d.ts +1 -1
- package/dist/analyzer/page-analyzer/types.d.ts.map +1 -1
- package/dist/api/client.d.ts +11 -11
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +27 -22
- package/dist/api/client.js.map +1 -1
- package/dist/detectors/action-description.d.ts +1 -1
- package/dist/detectors/action-description.d.ts.map +1 -1
- package/dist/detectors/action-description.js +1 -1
- package/dist/detectors/action-description.js.map +1 -1
- package/dist/detectors/index.d.ts +1 -1
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +1 -1
- package/dist/detectors/index.js.map +1 -1
- package/dist/domain/types.d.ts +1 -1
- package/dist/domain/types.d.ts.map +1 -1
- package/dist/generation/e2e.d.ts +2 -2
- package/dist/generation/e2e.d.ts.map +1 -1
- package/dist/generation/e2e.js +1 -1
- package/dist/generation/e2e.js.map +1 -1
- package/dist/generation/form-negative.d.ts +2 -2
- package/dist/generation/form-negative.d.ts.map +1 -1
- package/dist/generation/form-negative.js +1 -1
- package/dist/generation/form-negative.js.map +1 -1
- package/dist/generation/form.d.ts +2 -2
- package/dist/generation/form.d.ts.map +1 -1
- package/dist/generation/form.js +1 -1
- package/dist/generation/form.js.map +1 -1
- package/dist/generation/generator.d.ts +2 -2
- package/dist/generation/generator.d.ts.map +1 -1
- package/dist/generation/generator.js +1 -1
- package/dist/generation/generator.js.map +1 -1
- package/dist/generation/interaction.d.ts +2 -2
- package/dist/generation/interaction.d.ts.map +1 -1
- package/dist/generation/interaction.js +1 -1
- package/dist/generation/interaction.js.map +1 -1
- package/dist/generation/navigation.d.ts +2 -2
- package/dist/generation/navigation.d.ts.map +1 -1
- package/dist/generation/navigation.js +1 -1
- package/dist/generation/navigation.js.map +1 -1
- package/dist/generation/password.d.ts +4 -4
- package/dist/generation/password.d.ts.map +1 -1
- package/dist/generation/password.js +1 -1
- package/dist/generation/password.js.map +1 -1
- package/dist/generation/playwright-export.d.ts +2 -2
- package/dist/generation/playwright-export.d.ts.map +1 -1
- package/dist/generation/playwright-export.js +3 -3
- package/dist/generation/playwright-export.js.map +1 -1
- package/dist/generation/render.d.ts +7 -7
- package/dist/generation/render.d.ts.map +1 -1
- package/dist/generation/render.js +1 -1
- package/dist/generation/render.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/runner.d.ts.map +1 -1
- package/dist/orchestrator/runner.js +33 -27
- package/dist/orchestrator/runner.js.map +1 -1
- package/dist/orchestrator/{test-element-executor.d.ts → test-interaction-executor.d.ts} +3 -3
- package/dist/orchestrator/test-interaction-executor.d.ts.map +1 -0
- package/dist/orchestrator/{test-element-executor.js → test-interaction-executor.js} +55 -52
- package/dist/orchestrator/test-interaction-executor.js.map +1 -0
- package/dist/orchestrator/types.d.ts +3 -3
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/orchestrator/test-element-executor.d.ts.map +0 -1
- package/dist/orchestrator/test-element-executor.js.map +0 -1
|
@@ -4,16 +4,16 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { fillValuePlanner } from "../../planners/fill-value-planner";
|
|
5
5
|
import { AUTH_URL_PATTERNS, SIGNUP_URL_PATTERNS } from "../../config/constants";
|
|
6
6
|
import { generateHoverFollowUpCases } from "./generators/hover-follow-up";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
7
|
+
import { generateNavigationTestInteractions } from "./generators/navigation";
|
|
8
|
+
import { generateScaffoldTestInteractions } from "./generators/scaffolds";
|
|
9
|
+
import { generateRenderTestInteractions } from "./generators/render";
|
|
10
|
+
import { generateFormTestInteractions } from "./generators/forms";
|
|
11
|
+
import { generateE2ETestInteractions } from "./generators/e2e";
|
|
12
|
+
import { generateSemanticJourneyTestInteractions } from "./generators/semantic-journeys";
|
|
13
|
+
import { generateDialogLifecycleTestInteractions } from "./generators/dialogs";
|
|
14
|
+
import { generateKeyboardAndDisclosureTestInteractions } from "./generators/keyboard-disclosure";
|
|
15
|
+
import { generateVariantTestInteractions } from "./generators/variants";
|
|
16
|
+
import { generateContentTestInteractions } from "./generators/content";
|
|
17
17
|
/**
|
|
18
18
|
* PageAnalyzer generates expectations and discovers new test elements
|
|
19
19
|
* during discovery mode.
|
|
@@ -23,8 +23,10 @@ export class PageAnalyzer {
|
|
|
23
23
|
* Generate baseline expectations for a test element.
|
|
24
24
|
* Called BEFORE expertises evaluate.
|
|
25
25
|
*/
|
|
26
|
-
generateExpectations(
|
|
27
|
-
const steps = Array.isArray(
|
|
26
|
+
generateExpectations(testInteraction) {
|
|
27
|
+
const steps = Array.isArray(testInteraction.steps)
|
|
28
|
+
? testInteraction.steps
|
|
29
|
+
: [];
|
|
28
30
|
const expectations = [
|
|
29
31
|
{
|
|
30
32
|
expectationType: ExpectationType.PageLoaded,
|
|
@@ -56,27 +58,27 @@ export class PageAnalyzer {
|
|
|
56
58
|
* Generate new test elements for scaffolds and page content.
|
|
57
59
|
* Called AFTER expertises evaluate and the target page state is established.
|
|
58
60
|
*/
|
|
59
|
-
async
|
|
61
|
+
async generateTestInteractions(testInteraction, context) {
|
|
60
62
|
const normalizedContext = this.normalizeContext(context);
|
|
61
63
|
const currentPageStateId = await this.ensureTargetPageState(normalizedContext);
|
|
62
64
|
const resolvedContext = {
|
|
63
65
|
...normalizedContext,
|
|
64
66
|
currentPageStateId,
|
|
65
67
|
};
|
|
66
|
-
if (this.isHoverOnly(
|
|
67
|
-
await generateHoverFollowUpCases(this,
|
|
68
|
+
if (this.isHoverOnly(testInteraction)) {
|
|
69
|
+
await generateHoverFollowUpCases(this, testInteraction, resolvedContext);
|
|
68
70
|
return;
|
|
69
71
|
}
|
|
70
|
-
await
|
|
71
|
-
await
|
|
72
|
-
await
|
|
73
|
-
await
|
|
74
|
-
await
|
|
75
|
-
await
|
|
76
|
-
await
|
|
77
|
-
await
|
|
78
|
-
await
|
|
79
|
-
await
|
|
72
|
+
await generateRenderTestInteractions(this, resolvedContext);
|
|
73
|
+
await generateFormTestInteractions(this, resolvedContext);
|
|
74
|
+
await generateSemanticJourneyTestInteractions(this, resolvedContext);
|
|
75
|
+
await generateE2ETestInteractions(this, resolvedContext);
|
|
76
|
+
await generateDialogLifecycleTestInteractions(this, resolvedContext);
|
|
77
|
+
await generateScaffoldTestInteractions(this, resolvedContext);
|
|
78
|
+
await generateContentTestInteractions(this, resolvedContext);
|
|
79
|
+
await generateKeyboardAndDisclosureTestInteractions(this, resolvedContext);
|
|
80
|
+
await generateVariantTestInteractions(this, resolvedContext);
|
|
81
|
+
await generateNavigationTestInteractions(this, resolvedContext);
|
|
80
82
|
}
|
|
81
83
|
async reconcileGeneratedSurfaceElements(context, params) {
|
|
82
84
|
const surface = params.surfaceId != null
|
|
@@ -84,27 +86,27 @@ export class PageAnalyzer {
|
|
|
84
86
|
: await this.findExistingSurfaceByTitle(context, params.surfaceTitle);
|
|
85
87
|
if (!surface)
|
|
86
88
|
return;
|
|
87
|
-
const existing = await context.api.
|
|
89
|
+
const existing = await context.api.getTestInteractionsByTestSurface(surface.id, true);
|
|
88
90
|
const desiredKeys = new Set(params.desiredKeys.map(key => key.trim()).filter(Boolean));
|
|
89
91
|
const obsoleteIds = existing
|
|
90
|
-
.filter(
|
|
91
|
-
const isGenerated = Boolean(
|
|
92
|
-
const isActive =
|
|
92
|
+
.filter(testInteraction => {
|
|
93
|
+
const isGenerated = Boolean(testInteraction.isGenerated);
|
|
94
|
+
const isActive = testInteraction.isActive !== false;
|
|
93
95
|
if (!isGenerated || !isActive)
|
|
94
96
|
return false;
|
|
95
|
-
if (
|
|
97
|
+
if (testInteraction.startingPageStateId !== context.currentPageStateId)
|
|
96
98
|
return false;
|
|
97
|
-
if ((
|
|
98
|
-
(params.
|
|
99
|
+
if ((testInteraction.dependencyTestInteractionId ?? null) !==
|
|
100
|
+
(params.dependencyTestInteractionId ?? null)) {
|
|
99
101
|
return false;
|
|
100
102
|
}
|
|
101
|
-
const existingKey = this.getPersistedGeneratedKey(
|
|
103
|
+
const existingKey = this.getPersistedGeneratedKey(testInteraction);
|
|
102
104
|
return !existingKey || !desiredKeys.has(existingKey);
|
|
103
105
|
})
|
|
104
|
-
.map(
|
|
106
|
+
.map(testInteraction => testInteraction.id);
|
|
105
107
|
if (obsoleteIds.length === 0)
|
|
106
108
|
return;
|
|
107
|
-
await context.api.
|
|
109
|
+
await context.api.retireTestInteractions(obsoleteIds);
|
|
108
110
|
}
|
|
109
111
|
async findExistingSurfaceByTitle(context, title) {
|
|
110
112
|
const surfaces = await context.api.getTestSurfacesByRunner(context.runnerId);
|
|
@@ -149,7 +151,7 @@ export class PageAnalyzer {
|
|
|
149
151
|
return null;
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
|
-
|
|
154
|
+
buildNavigationTestInteraction(path, sizeClass, uid, startingPageStateId) {
|
|
153
155
|
return {
|
|
154
156
|
title: `Navigate to ${path}`,
|
|
155
157
|
type: "navigation",
|
|
@@ -176,7 +178,7 @@ export class PageAnalyzer {
|
|
|
176
178
|
generatedKey: this.buildGeneratedKey("navigation", startingPageStateId, path),
|
|
177
179
|
};
|
|
178
180
|
}
|
|
179
|
-
|
|
181
|
+
buildHoverTestInteraction(item, startingPath, sizeClass, uid, startingPageStateId, dependencyTestInteractionId) {
|
|
180
182
|
const label = this.describeActionableItem(item);
|
|
181
183
|
return {
|
|
182
184
|
title: `Hover over ${label}`,
|
|
@@ -184,7 +186,7 @@ export class PageAnalyzer {
|
|
|
184
186
|
sizeClass,
|
|
185
187
|
surface_tags: ["interaction", "hover"],
|
|
186
188
|
priority: 4,
|
|
187
|
-
|
|
189
|
+
dependencyTestInteractionId,
|
|
188
190
|
startingPageStateId,
|
|
189
191
|
startingPath,
|
|
190
192
|
steps: [
|
|
@@ -202,10 +204,10 @@ export class PageAnalyzer {
|
|
|
202
204
|
],
|
|
203
205
|
globalExpectations: [],
|
|
204
206
|
uid,
|
|
205
|
-
generatedKey: this.buildGeneratedKey("hover", startingPageStateId,
|
|
207
|
+
generatedKey: this.buildGeneratedKey("hover", startingPageStateId, dependencyTestInteractionId, item.stableKey ?? item.selector ?? label),
|
|
206
208
|
};
|
|
207
209
|
}
|
|
208
|
-
|
|
210
|
+
buildClickTestInteraction(item, startingPath, sizeClass, uid, startingPageStateId, dependencyTestInteractionId) {
|
|
209
211
|
const label = this.describeActionableItem(item);
|
|
210
212
|
return {
|
|
211
213
|
title: `Click ${label}`,
|
|
@@ -213,7 +215,7 @@ export class PageAnalyzer {
|
|
|
213
215
|
sizeClass,
|
|
214
216
|
surface_tags: ["interaction", "click"],
|
|
215
217
|
priority: 5,
|
|
216
|
-
|
|
218
|
+
dependencyTestInteractionId,
|
|
217
219
|
startingPageStateId,
|
|
218
220
|
startingPath,
|
|
219
221
|
steps: [
|
|
@@ -231,16 +233,20 @@ export class PageAnalyzer {
|
|
|
231
233
|
],
|
|
232
234
|
globalExpectations: [],
|
|
233
235
|
uid,
|
|
234
|
-
generatedKey: this.buildGeneratedKey("click", startingPageStateId,
|
|
236
|
+
generatedKey: this.buildGeneratedKey("click", startingPageStateId, dependencyTestInteractionId, item.stableKey ?? item.selector ?? label),
|
|
235
237
|
};
|
|
236
238
|
}
|
|
237
|
-
isHoverOnly(
|
|
238
|
-
const steps = Array.isArray(
|
|
239
|
+
isHoverOnly(testInteraction) {
|
|
240
|
+
const steps = Array.isArray(testInteraction.steps)
|
|
241
|
+
? testInteraction.steps
|
|
242
|
+
: [];
|
|
239
243
|
return (steps.length === 1 &&
|
|
240
244
|
steps[0]?.action?.actionType === PlaywrightAction.Hover);
|
|
241
245
|
}
|
|
242
|
-
getPrimarySelector(
|
|
243
|
-
const steps = Array.isArray(
|
|
246
|
+
getPrimarySelector(testInteraction) {
|
|
247
|
+
const steps = Array.isArray(testInteraction.steps)
|
|
248
|
+
? testInteraction.steps
|
|
249
|
+
: [];
|
|
244
250
|
const step = steps[0];
|
|
245
251
|
return step?.action.path ?? null;
|
|
246
252
|
}
|
|
@@ -249,20 +255,20 @@ export class PageAnalyzer {
|
|
|
249
255
|
const selector = item.selector;
|
|
250
256
|
return stableKey ?? selector ?? null;
|
|
251
257
|
}
|
|
252
|
-
withGeneratedKey(
|
|
258
|
+
withGeneratedKey(testInteraction, ...parts) {
|
|
253
259
|
return {
|
|
254
|
-
...
|
|
260
|
+
...testInteraction,
|
|
255
261
|
generatedKey: this.buildGeneratedKey(...parts),
|
|
256
262
|
};
|
|
257
263
|
}
|
|
258
|
-
getGeneratedKey(
|
|
259
|
-
return (
|
|
264
|
+
getGeneratedKey(testInteraction) {
|
|
265
|
+
return (testInteraction.generatedKey?.trim() || testInteraction.title).trim();
|
|
260
266
|
}
|
|
261
|
-
getPersistedGeneratedKey(
|
|
262
|
-
const generatedKey =
|
|
267
|
+
getPersistedGeneratedKey(testInteraction) {
|
|
268
|
+
const generatedKey = testInteraction.generatedKey?.trim();
|
|
263
269
|
if (generatedKey)
|
|
264
270
|
return generatedKey;
|
|
265
|
-
const title =
|
|
271
|
+
const title = testInteraction.title?.trim();
|
|
266
272
|
return title || null;
|
|
267
273
|
}
|
|
268
274
|
buildGeneratedKey(...parts) {
|
|
@@ -396,7 +402,7 @@ export class PageAnalyzer {
|
|
|
396
402
|
normalizeForms(forms) {
|
|
397
403
|
return Array.isArray(forms) ? forms : [];
|
|
398
404
|
}
|
|
399
|
-
|
|
405
|
+
buildRenderTestInteraction(currentPath, sizeClass, uid, startingPageStateId, pageId) {
|
|
400
406
|
return {
|
|
401
407
|
title: `Render — ${currentPath}`,
|
|
402
408
|
type: "render",
|
|
@@ -452,7 +458,7 @@ export class PageAnalyzer {
|
|
|
452
458
|
generatedKey: this.buildGeneratedKey("render", startingPageStateId, pageId, currentPath),
|
|
453
459
|
};
|
|
454
460
|
}
|
|
455
|
-
|
|
461
|
+
buildFormTestInteraction(form, formLabel, formType, currentPath, sizeClass, uid, startingPageStateId, validValues) {
|
|
456
462
|
const steps = this.buildFormSteps(form, validValues, undefined);
|
|
457
463
|
return {
|
|
458
464
|
title: `Form — ${formLabel}`,
|
|
@@ -497,7 +503,7 @@ export class PageAnalyzer {
|
|
|
497
503
|
generatedKey: this.buildGeneratedKey("form-positive", startingPageStateId, form.selector, formType),
|
|
498
504
|
};
|
|
499
505
|
}
|
|
500
|
-
|
|
506
|
+
buildNegativeFormTestInteraction(form, formLabel, formType, omittedField, currentPath, sizeClass, uid, startingPageStateId, validValues) {
|
|
501
507
|
const steps = this.buildFormSteps(form, validValues, omittedField.selector, this.buildValuePreservationExpectations(form, validValues, omittedField.selector));
|
|
502
508
|
return {
|
|
503
509
|
title: `Form Negative — ${formLabel} (missing ${this.fieldLabel(omittedField)})`,
|
|
@@ -520,7 +526,7 @@ export class PageAnalyzer {
|
|
|
520
526
|
generatedKey: this.buildGeneratedKey("form-negative", startingPageStateId, form.selector, formType, omittedField.selector),
|
|
521
527
|
};
|
|
522
528
|
}
|
|
523
|
-
|
|
529
|
+
buildFormCorrectionTestInteraction(form, formLabel, formType, correctedField, currentPath, sizeClass, uid, startingPageStateId, validValues) {
|
|
524
530
|
const steps = this.buildFormSteps(form, validValues, correctedField.selector, this.buildValuePreservationExpectations(form, validValues, correctedField.selector));
|
|
525
531
|
const correctionValue = validValues[correctedField.selector];
|
|
526
532
|
if (correctionValue) {
|
|
@@ -565,7 +571,7 @@ export class PageAnalyzer {
|
|
|
565
571
|
generatedKey: this.buildGeneratedKey("form-correction", startingPageStateId, form.selector, formType, correctedField.selector),
|
|
566
572
|
};
|
|
567
573
|
}
|
|
568
|
-
|
|
574
|
+
buildPasswordTestInteractions(form, formLabel, formType, currentPath, sizeClass, uid, startingPageStateId, validValues, passwordRequirements) {
|
|
569
575
|
const passwordFields = form.fields.filter(field => field.type === "password");
|
|
570
576
|
if (passwordFields.length === 0)
|
|
571
577
|
return [];
|
|
@@ -602,7 +608,7 @@ export class PageAnalyzer {
|
|
|
602
608
|
};
|
|
603
609
|
});
|
|
604
610
|
}
|
|
605
|
-
|
|
611
|
+
buildE2ETestInteraction(currentPath, sizeClass, uid, startingPageStateId, journeySteps) {
|
|
606
612
|
return {
|
|
607
613
|
title: `E2E — Journey to ${currentPath}`,
|
|
608
614
|
type: "e2e",
|
|
@@ -864,7 +870,7 @@ export class PageAnalyzer {
|
|
|
864
870
|
.toLowerCase();
|
|
865
871
|
return field.type === "search" || /\bsearch\b/.test(text);
|
|
866
872
|
}
|
|
867
|
-
|
|
873
|
+
buildSearchTestInteractions(form, formLabel, currentPath, sizeClass, uid, startingPageStateId, validValues, actionableItems) {
|
|
868
874
|
const searchField = form.fields.find(field => this.isSearchField(field));
|
|
869
875
|
if (!searchField)
|
|
870
876
|
return [];
|
|
@@ -998,7 +1004,7 @@ export class PageAnalyzer {
|
|
|
998
1004
|
const descriptor = namedField?.label || namedField?.name || `form ${index + 1}`;
|
|
999
1005
|
return `${descriptor} @ ${form.selector}`;
|
|
1000
1006
|
}
|
|
1001
|
-
|
|
1007
|
+
buildSemanticJourneyTestInteractions(context) {
|
|
1002
1008
|
const items = this.selectRepresentativeItems(context.actionableItems.filter(item => item.visible && !item.disabled && Boolean(item.selector)));
|
|
1003
1009
|
const journeys = [];
|
|
1004
1010
|
const collectionCount = this.estimateCollectionCount(context.html);
|
|
@@ -1006,7 +1012,7 @@ export class PageAnalyzer {
|
|
|
1006
1012
|
const checkout = items.find(item => this.isCheckoutItem(item));
|
|
1007
1013
|
const createCollectionAction = items.find(item => this.isCreateCollectionAction(item));
|
|
1008
1014
|
if (addToCart && checkout) {
|
|
1009
|
-
journeys.push(this.
|
|
1015
|
+
journeys.push(this.buildJourneyTestInteraction("Commerce journey", ["commerce", "cart", "checkout"], context, [
|
|
1010
1016
|
this.buildJourneyAction(addToCart, "Add item to cart", [
|
|
1011
1017
|
this.makeExpectation("navigation_or_state_changed", "Adding an item to cart should update the page state"),
|
|
1012
1018
|
this.makeExpectation("count_changed", "Adding an item should update a visible count", {
|
|
@@ -1076,7 +1082,7 @@ export class PageAnalyzer {
|
|
|
1076
1082
|
severity: ExpectationSeverity.ShouldPass,
|
|
1077
1083
|
}));
|
|
1078
1084
|
}
|
|
1079
|
-
journeys.push(this.
|
|
1085
|
+
journeys.push(this.buildJourneyTestInteraction("Remove item from collection", ["commerce", "remove"], context, [
|
|
1080
1086
|
this.buildJourneyAction(removeItem, "Remove item", removeExpectations),
|
|
1081
1087
|
]));
|
|
1082
1088
|
}
|
|
@@ -1096,13 +1102,13 @@ export class PageAnalyzer {
|
|
|
1096
1102
|
this.makeExpectation("loading_completes", "Create/add flow should complete loading"),
|
|
1097
1103
|
this.makeExpectation("page_responsive", "Page should remain responsive after creating or adding a record"),
|
|
1098
1104
|
];
|
|
1099
|
-
journeys.push(this.
|
|
1105
|
+
journeys.push(this.buildJourneyTestInteraction("Create or add collection record", ["list", "crud", "create"], context, [
|
|
1100
1106
|
this.buildJourneyAction(createCollectionAction, "Create or add record", createExpectations),
|
|
1101
1107
|
]));
|
|
1102
1108
|
}
|
|
1103
1109
|
const authEntry = items.find(item => this.isAuthEntryItem(item));
|
|
1104
1110
|
if (authEntry) {
|
|
1105
|
-
journeys.push(this.
|
|
1111
|
+
journeys.push(this.buildJourneyTestInteraction("Authentication entry journey", ["auth"], context, [
|
|
1106
1112
|
this.buildJourneyAction(authEntry, "Open authentication entry point", [
|
|
1107
1113
|
this.makeExpectation("navigation_or_state_changed", "Authentication entry should open the next auth state"),
|
|
1108
1114
|
this.makeExpectation("loading_completes", "Authentication entry flow should settle"),
|
|
@@ -1112,7 +1118,7 @@ export class PageAnalyzer {
|
|
|
1112
1118
|
}
|
|
1113
1119
|
const protectedAction = items.find(item => item !== authEntry && this.isProtectedActionItem(item));
|
|
1114
1120
|
if (authEntry && protectedAction) {
|
|
1115
|
-
journeys.push(this.
|
|
1121
|
+
journeys.push(this.buildJourneyTestInteraction("Protected action auth gate journey", ["auth", "protected"], context, [
|
|
1116
1122
|
this.buildJourneyAction(protectedAction, "Open protected action", [
|
|
1117
1123
|
this.makeExpectation(ExpectationType.NavigationOrStateChanged, "Protected action should open a gated state, redirect, or login requirement"),
|
|
1118
1124
|
this.makeExpectation(ExpectationType.PageResponsive, "Page should remain responsive when auth gating is triggered"),
|
|
@@ -1122,7 +1128,7 @@ export class PageAnalyzer {
|
|
|
1122
1128
|
}
|
|
1123
1129
|
const logoutAction = items.find(item => this.isLogoutAction(item));
|
|
1124
1130
|
if (logoutAction) {
|
|
1125
|
-
journeys.push(this.
|
|
1131
|
+
journeys.push(this.buildJourneyTestInteraction("Logout journey", ["auth", "logout"], context, [
|
|
1126
1132
|
this.buildJourneyAction(logoutAction, "Log out", [
|
|
1127
1133
|
this.makeExpectation(ExpectationType.NavigationOrStateChanged, "Logging out should change application state"),
|
|
1128
1134
|
this.makeExpectation(ExpectationType.PageResponsive, "Page should remain responsive during logout"),
|
|
@@ -1138,7 +1144,7 @@ export class PageAnalyzer {
|
|
|
1138
1144
|
}
|
|
1139
1145
|
const retryAction = items.find(item => this.isRetryAction(item));
|
|
1140
1146
|
if (retryAction) {
|
|
1141
|
-
journeys.push(this.
|
|
1147
|
+
journeys.push(this.buildJourneyTestInteraction("Retry recovery journey", ["recovery", "retry"], context, [
|
|
1142
1148
|
this.buildJourneyAction(retryAction, "Retry failed action", [
|
|
1143
1149
|
this.makeExpectation(ExpectationType.ErrorStateCleared, "Retrying should clear any visible recoverable error state"),
|
|
1144
1150
|
this.makeExpectation(ExpectationType.NetworkRequestMade, "Retrying should trigger a follow-up request or state transition", {
|
|
@@ -1164,7 +1170,7 @@ export class PageAnalyzer {
|
|
|
1164
1170
|
if (this.isVideoLikeItem(mediaCandidate)) {
|
|
1165
1171
|
mediaExpectations.push(this.makeExpectation("video_playable", "Opened video should be playable"));
|
|
1166
1172
|
}
|
|
1167
|
-
journeys.push(this.
|
|
1173
|
+
journeys.push(this.buildJourneyTestInteraction("Media open journey", ["media"], context, [
|
|
1168
1174
|
this.buildJourneyAction(mediaCandidate, "Open media", mediaExpectations),
|
|
1169
1175
|
]));
|
|
1170
1176
|
}
|
|
@@ -1185,7 +1191,7 @@ export class PageAnalyzer {
|
|
|
1185
1191
|
this.makeExpectation("loading_completes", "Quantity adjustment should complete loading"),
|
|
1186
1192
|
this.makeExpectation("page_responsive", "Page should remain responsive during quantity adjustment"),
|
|
1187
1193
|
].filter(Boolean);
|
|
1188
|
-
journeys.push(this.
|
|
1194
|
+
journeys.push(this.buildJourneyTestInteraction(quantityDelta === -1
|
|
1189
1195
|
? "Quantity decrease journey"
|
|
1190
1196
|
: quantityDelta === 1
|
|
1191
1197
|
? "Quantity increase journey"
|
|
@@ -1199,14 +1205,14 @@ export class PageAnalyzer {
|
|
|
1199
1205
|
}
|
|
1200
1206
|
const filterAction = items.find(item => this.isFilterAction(item));
|
|
1201
1207
|
if (filterAction) {
|
|
1202
|
-
journeys.push(this.
|
|
1208
|
+
journeys.push(this.buildJourneyTestInteraction("Filter results journey", ["filter"], context, [
|
|
1203
1209
|
this.buildJourneyAction(filterAction, "Apply filter", [
|
|
1204
1210
|
this.makeExpectation("navigation_or_state_changed", "Applying a filter should change the result state"),
|
|
1205
1211
|
this.makeExpectation("results_changed", "Applying a filter should change the visible result summary"),
|
|
1206
1212
|
this.makeExpectation("loading_completes", "Filter update should complete loading"),
|
|
1207
1213
|
]),
|
|
1208
1214
|
]));
|
|
1209
|
-
journeys.push(this.
|
|
1215
|
+
journeys.push(this.buildJourneyTestInteraction("Filter persistence journey", ["filter", "reload"], context, [
|
|
1210
1216
|
this.buildJourneyAction(filterAction, "Apply filter", [
|
|
1211
1217
|
this.makeExpectation("navigation_or_state_changed", "Applying a filter should change the result state"),
|
|
1212
1218
|
]),
|
|
@@ -1220,7 +1226,7 @@ export class PageAnalyzer {
|
|
|
1220
1226
|
}
|
|
1221
1227
|
const sortAction = items.find(item => this.isSortAction(item));
|
|
1222
1228
|
if (sortAction) {
|
|
1223
|
-
journeys.push(this.
|
|
1229
|
+
journeys.push(this.buildJourneyTestInteraction("Sort results journey", ["sort"], context, [
|
|
1224
1230
|
this.buildJourneyAction(sortAction, "Change sort order", [
|
|
1225
1231
|
this.makeExpectation("navigation_or_state_changed", "Changing sort should update the result state"),
|
|
1226
1232
|
this.makeExpectation("collection_order_changed", "Changing sort should change the visible collection ordering"),
|
|
@@ -1230,7 +1236,7 @@ export class PageAnalyzer {
|
|
|
1230
1236
|
}
|
|
1231
1237
|
const paginationAction = items.find(item => this.isPaginationAction(item));
|
|
1232
1238
|
if (paginationAction) {
|
|
1233
|
-
journeys.push(this.
|
|
1239
|
+
journeys.push(this.buildJourneyTestInteraction("Pagination journey", ["list", "pagination"], context, [
|
|
1234
1240
|
this.buildJourneyAction(paginationAction, "Paginate list", [
|
|
1235
1241
|
this.makeExpectation(ExpectationType.NavigationOrStateChanged, "Pagination should change the visible list state"),
|
|
1236
1242
|
this.makeExpectation("results_changed", "Pagination should change the visible collection contents"),
|
|
@@ -1241,7 +1247,7 @@ export class PageAnalyzer {
|
|
|
1241
1247
|
]));
|
|
1242
1248
|
}
|
|
1243
1249
|
if (addToCart) {
|
|
1244
|
-
journeys.push(this.
|
|
1250
|
+
journeys.push(this.buildJourneyTestInteraction("Cart persistence journey", ["commerce", "reload"], context, [
|
|
1245
1251
|
this.buildJourneyAction(addToCart, "Add item to cart", [
|
|
1246
1252
|
this.makeExpectation("navigation_or_state_changed", "Adding an item should change page state before reload"),
|
|
1247
1253
|
]),
|
|
@@ -1260,7 +1266,7 @@ export class PageAnalyzer {
|
|
|
1260
1266
|
]));
|
|
1261
1267
|
}
|
|
1262
1268
|
if (collectionCount > 0 && removeItem && createCollectionAction) {
|
|
1263
|
-
journeys.push(this.
|
|
1269
|
+
journeys.push(this.buildJourneyTestInteraction("Collection mutation recovery journey", ["list", "crud", "recovery"], context, [
|
|
1264
1270
|
this.buildJourneyAction(removeItem, "Remove record", [
|
|
1265
1271
|
this.makeExpectation("row_count_changed", "Removing a record should change the visible collection", {
|
|
1266
1272
|
expectedCountDelta: -1,
|
|
@@ -1281,7 +1287,7 @@ export class PageAnalyzer {
|
|
|
1281
1287
|
this.isMediaOpenItem(item) ||
|
|
1282
1288
|
this.isAuthEntryItem(item));
|
|
1283
1289
|
if (backCandidate) {
|
|
1284
|
-
journeys.push(this.
|
|
1290
|
+
journeys.push(this.buildJourneyTestInteraction("Back and forward navigation journey", ["navigation", "history"], context, [
|
|
1285
1291
|
this.buildJourneyAction(backCandidate, "Navigate to next state", [
|
|
1286
1292
|
this.makeExpectation("navigation_or_state_changed", "Navigation should move to a different state"),
|
|
1287
1293
|
]),
|
|
@@ -1297,7 +1303,7 @@ export class PageAnalyzer {
|
|
|
1297
1303
|
}
|
|
1298
1304
|
return journeys;
|
|
1299
1305
|
}
|
|
1300
|
-
|
|
1306
|
+
buildJourneyTestInteraction(title, surfaceTags, context, steps) {
|
|
1301
1307
|
return {
|
|
1302
1308
|
title: `${title} — ${context.currentPath}`,
|
|
1303
1309
|
type: "e2e",
|
|
@@ -1414,18 +1420,18 @@ export class PageAnalyzer {
|
|
|
1414
1420
|
isCheckboxLike(field) {
|
|
1415
1421
|
return field.type === "checkbox" || field.type === "radio";
|
|
1416
1422
|
}
|
|
1417
|
-
|
|
1423
|
+
buildKeyboardAndDisclosureTestInteractions(context) {
|
|
1418
1424
|
const items = this.selectRepresentativeItems(context.actionableItems.filter(item => item.visible && !item.disabled && Boolean(item.selector)));
|
|
1419
1425
|
const tests = [];
|
|
1420
1426
|
for (const item of items) {
|
|
1421
1427
|
if (this.isDisclosureItem(item)) {
|
|
1422
|
-
tests.push(this.
|
|
1423
|
-
tests.push(this.
|
|
1428
|
+
tests.push(this.buildDisclosureToggleTestInteraction(item, context.currentPath, context.sizeClass, context.uid, context.currentPageStateId));
|
|
1429
|
+
tests.push(this.buildKeyboardActivateTestInteraction(item, "Enter", "Activate disclosure with Enter", [
|
|
1424
1430
|
this.makeExpectation("expanded_state_changed", "Enter should toggle the disclosure state", {
|
|
1425
1431
|
targetPath: item.selector,
|
|
1426
1432
|
}),
|
|
1427
1433
|
], context.currentPath, context.sizeClass, context.uid, context.currentPageStateId));
|
|
1428
|
-
tests.push(this.
|
|
1434
|
+
tests.push(this.buildKeyboardActivateTestInteraction(item, " ", "Activate disclosure with Space", [
|
|
1429
1435
|
this.makeExpectation("expanded_state_changed", "Space should toggle the disclosure state", {
|
|
1430
1436
|
targetPath: item.selector,
|
|
1431
1437
|
}),
|
|
@@ -1433,12 +1439,12 @@ export class PageAnalyzer {
|
|
|
1433
1439
|
continue;
|
|
1434
1440
|
}
|
|
1435
1441
|
if (this.isKeyboardPrimaryAction(item)) {
|
|
1436
|
-
tests.push(this.
|
|
1442
|
+
tests.push(this.buildKeyboardActivateTestInteraction(item, "Enter", "Activate with Enter", [
|
|
1437
1443
|
this.makeExpectation("navigation_or_state_changed", "Enter key activation should change the page or control state"),
|
|
1438
1444
|
], context.currentPath, context.sizeClass, context.uid, context.currentPageStateId));
|
|
1439
1445
|
}
|
|
1440
1446
|
if (this.isKeyboardToggleAction(item)) {
|
|
1441
|
-
tests.push(this.
|
|
1447
|
+
tests.push(this.buildKeyboardActivateTestInteraction(item, " ", "Toggle with Space", [
|
|
1442
1448
|
this.makeExpectation(ExpectationType.ElementChecked, "Space key activation should toggle the control state", {
|
|
1443
1449
|
targetPath: item.selector,
|
|
1444
1450
|
}),
|
|
@@ -1447,13 +1453,13 @@ export class PageAnalyzer {
|
|
|
1447
1453
|
}
|
|
1448
1454
|
return tests;
|
|
1449
1455
|
}
|
|
1450
|
-
|
|
1456
|
+
buildVariantTestInteractions(context) {
|
|
1451
1457
|
const items = this.selectRepresentativeItems(context.actionableItems.filter(item => item.visible &&
|
|
1452
1458
|
!item.disabled &&
|
|
1453
1459
|
Boolean(item.selector) &&
|
|
1454
1460
|
this.isVariantSelector(item)));
|
|
1455
1461
|
const tests = items
|
|
1456
|
-
.map(item => this.
|
|
1462
|
+
.map(item => this.buildVariantTestInteraction(item, context))
|
|
1457
1463
|
.filter((item) => Boolean(item));
|
|
1458
1464
|
const purchaseAction = context.actionableItems.find(item => item.visible &&
|
|
1459
1465
|
!item.disabled &&
|
|
@@ -1465,12 +1471,12 @@ export class PageAnalyzer {
|
|
|
1465
1471
|
tests.push(purchaseJourney);
|
|
1466
1472
|
const requiredField = this.findRequiredVariantField(item, context.forms);
|
|
1467
1473
|
if (requiredField && purchaseAction) {
|
|
1468
|
-
tests.push(this.
|
|
1474
|
+
tests.push(this.buildRequiredVariantGuardTestInteraction(item, requiredField, purchaseAction, context));
|
|
1469
1475
|
}
|
|
1470
1476
|
}
|
|
1471
1477
|
return tests;
|
|
1472
1478
|
}
|
|
1473
|
-
|
|
1479
|
+
buildVariantTestInteraction(item, context) {
|
|
1474
1480
|
const plannedValue = this.extractSelectableValue(item);
|
|
1475
1481
|
if (!plannedValue || !item.selector)
|
|
1476
1482
|
return null;
|
|
@@ -1583,7 +1589,7 @@ export class PageAnalyzer {
|
|
|
1583
1589
|
generatedKey: this.buildGeneratedKey("variant-purchase", context.currentPageStateId, item.selector, plannedValue, purchaseAction.selector),
|
|
1584
1590
|
};
|
|
1585
1591
|
}
|
|
1586
|
-
|
|
1592
|
+
buildRequiredVariantGuardTestInteraction(item, requiredField, purchaseAction, context) {
|
|
1587
1593
|
const label = this.describeActionableItem(item);
|
|
1588
1594
|
const purchaseLabel = this.describeActionableItem(purchaseAction);
|
|
1589
1595
|
return {
|
|
@@ -1617,7 +1623,7 @@ export class PageAnalyzer {
|
|
|
1617
1623
|
}
|
|
1618
1624
|
return undefined;
|
|
1619
1625
|
}
|
|
1620
|
-
|
|
1626
|
+
buildDisclosureToggleTestInteraction(item, startingPath, sizeClass, uid, startingPageStateId) {
|
|
1621
1627
|
const label = this.describeActionableItem(item);
|
|
1622
1628
|
return {
|
|
1623
1629
|
title: `Toggle disclosure ${label}`,
|
|
@@ -1649,7 +1655,7 @@ export class PageAnalyzer {
|
|
|
1649
1655
|
generatedKey: this.buildGeneratedKey("disclosure-click", startingPageStateId, item.stableKey ?? item.selector ?? label),
|
|
1650
1656
|
};
|
|
1651
1657
|
}
|
|
1652
|
-
|
|
1658
|
+
buildKeyboardActivateTestInteraction(item, key, titlePrefix, expectations, startingPath, sizeClass, uid, startingPageStateId) {
|
|
1653
1659
|
const label = this.describeActionableItem(item);
|
|
1654
1660
|
return {
|
|
1655
1661
|
title: `${titlePrefix} ${label}`,
|
|
@@ -1695,7 +1701,7 @@ export class PageAnalyzer {
|
|
|
1695
1701
|
generatedKey: this.buildGeneratedKey("keyboard", startingPageStateId, key === " " ? "space" : key, item.stableKey ?? item.selector ?? label),
|
|
1696
1702
|
};
|
|
1697
1703
|
}
|
|
1698
|
-
|
|
1704
|
+
buildDialogCloseTestInteraction(item, startingPath, sizeClass, uid, startingPageStateId) {
|
|
1699
1705
|
const label = this.describeActionableItem(item);
|
|
1700
1706
|
return {
|
|
1701
1707
|
title: `Close dialog via ${label}`,
|
|
@@ -1726,7 +1732,7 @@ export class PageAnalyzer {
|
|
|
1726
1732
|
generatedKey: this.buildGeneratedKey("dialog-close", startingPageStateId, item.stableKey ?? item.selector ?? label),
|
|
1727
1733
|
};
|
|
1728
1734
|
}
|
|
1729
|
-
|
|
1735
|
+
buildEscapeDialogTestInteraction(startingPath, sizeClass, uid, startingPageStateId) {
|
|
1730
1736
|
return {
|
|
1731
1737
|
title: "Close dialog with Escape",
|
|
1732
1738
|
type: "interaction",
|
|
@@ -1770,7 +1776,7 @@ export class PageAnalyzer {
|
|
|
1770
1776
|
inputType === "radio" ||
|
|
1771
1777
|
inputType === "checkbox");
|
|
1772
1778
|
}
|
|
1773
|
-
|
|
1779
|
+
buildControlInteractionTestInteraction(item, startingPath, sizeClass, uid, startingPageStateId) {
|
|
1774
1780
|
const label = this.describeActionableItem(item);
|
|
1775
1781
|
const role = (item.role ?? "").toLowerCase();
|
|
1776
1782
|
const inputType = (item.inputType ?? "").toLowerCase();
|