@sudobility/testomniac_runner_service 0.1.52 → 0.1.54
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 +3 -0
- package/dist/analyzer/page-analyzer/generators/content.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/content.js +34 -0
- package/dist/analyzer/page-analyzer/generators/content.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/dialogs.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/dialogs.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/dialogs.js +38 -0
- package/dist/analyzer/page-analyzer/generators/dialogs.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/e2e.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/e2e.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/e2e.js +28 -0
- package/dist/analyzer/page-analyzer/generators/e2e.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/forms.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/forms.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/forms.js +69 -0
- package/dist/analyzer/page-analyzer/generators/forms.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.d.ts +4 -0
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.js +34 -0
- package/dist/analyzer/page-analyzer/generators/hover-follow-up.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js +30 -0
- package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/navigation.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/navigation.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/navigation.js +22 -0
- package/dist/analyzer/page-analyzer/generators/navigation.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/render.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/render.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/render.js +26 -0
- package/dist/analyzer/page-analyzer/generators/render.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/scaffolds.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/scaffolds.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/scaffolds.js +36 -0
- package/dist/analyzer/page-analyzer/generators/scaffolds.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.js +30 -0
- package/dist/analyzer/page-analyzer/generators/semantic-journeys.js.map +1 -0
- package/dist/analyzer/page-analyzer/generators/variants.d.ts +3 -0
- package/dist/analyzer/page-analyzer/generators/variants.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/generators/variants.js +30 -0
- package/dist/analyzer/page-analyzer/generators/variants.js.map +1 -0
- package/dist/analyzer/{page-analyzer.d.ts → page-analyzer/index.d.ts} +9 -39
- package/dist/analyzer/page-analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/{page-analyzer.js → page-analyzer/index.js} +118 -398
- package/dist/analyzer/page-analyzer/index.js.map +1 -0
- package/dist/analyzer/page-analyzer/types.d.ts +28 -0
- package/dist/analyzer/page-analyzer/types.d.ts.map +1 -0
- package/dist/analyzer/page-analyzer/types.js +1 -0
- package/dist/analyzer/page-analyzer/types.js.map +1 -0
- package/dist/browser/dom-snapshot.d.ts.map +1 -1
- package/dist/browser/dom-snapshot.js +84 -0
- package/dist/browser/dom-snapshot.js.map +1 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +3 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/orchestrator/runner.d.ts.map +1 -1
- package/dist/orchestrator/runner.js +19 -5
- package/dist/orchestrator/runner.js.map +1 -1
- package/dist/orchestrator/types.d.ts +2 -0
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/analyzer/page-analyzer.d.ts.map +0 -1
- package/dist/analyzer/page-analyzer.js.map +0 -1
|
@@ -1,30 +1,6 @@
|
|
|
1
|
-
import type { TestElement, Expectation
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
import type { ScanEventHandler } from "../orchestrator/types";
|
|
5
|
-
export interface AnalyzerContext {
|
|
6
|
-
runnerId: number;
|
|
7
|
-
sizeClass: SizeClass;
|
|
8
|
-
uid?: string;
|
|
9
|
-
currentTestElementId: number;
|
|
10
|
-
currentTestSurfaceId: number;
|
|
11
|
-
currentSurfaceRunId: number | null;
|
|
12
|
-
html: string;
|
|
13
|
-
currentPageStateId: number;
|
|
14
|
-
beginningPageStateId: number;
|
|
15
|
-
currentPath: string;
|
|
16
|
-
pageId: number;
|
|
17
|
-
pageRequiresLogin: boolean;
|
|
18
|
-
scaffolds: DetectedScaffoldRegion[];
|
|
19
|
-
scaffoldSelectorByItemSelector: Record<string, string>;
|
|
20
|
-
actionableItems: ActionableItem[];
|
|
21
|
-
forms: FormInfo[];
|
|
22
|
-
journeySteps: TestStep[];
|
|
23
|
-
navigationSurface: TestSurfaceResponse;
|
|
24
|
-
bundleRun: TestSurfaceBundleRunResponse;
|
|
25
|
-
api: ApiClient;
|
|
26
|
-
events: ScanEventHandler;
|
|
27
|
-
}
|
|
1
|
+
import type { TestElement, Expectation } from "@sudobility/testomniac_types";
|
|
2
|
+
import type { AnalyzerContext } from "./types";
|
|
3
|
+
export type { AnalyzerContext } from "./types";
|
|
28
4
|
/**
|
|
29
5
|
* PageAnalyzer generates expectations and discovers new test elements
|
|
30
6
|
* during discovery mode.
|
|
@@ -40,17 +16,7 @@ export declare class PageAnalyzer {
|
|
|
40
16
|
* Called AFTER expertises evaluate and the target page state is established.
|
|
41
17
|
*/
|
|
42
18
|
generateTestElements(testElement: TestElement, context: AnalyzerContext): Promise<void>;
|
|
43
|
-
private
|
|
44
|
-
private generateNavigationTestElements;
|
|
45
|
-
private generateScaffoldTestElements;
|
|
46
|
-
private generateRenderTestElements;
|
|
47
|
-
private generateFormTestElements;
|
|
48
|
-
private generateE2ETestElements;
|
|
49
|
-
private generateSemanticJourneyTestElements;
|
|
50
|
-
private generateDialogLifecycleTestElements;
|
|
51
|
-
private generateKeyboardAndDisclosureTestElements;
|
|
52
|
-
private generateVariantTestElements;
|
|
53
|
-
private generateContentTestElements;
|
|
19
|
+
private getScaffoldSurfaceItems;
|
|
54
20
|
private ensureSurfaceRun;
|
|
55
21
|
private isMouseActionable;
|
|
56
22
|
private isSurfaceCandidate;
|
|
@@ -113,6 +79,10 @@ export declare class PageAnalyzer {
|
|
|
113
79
|
private hasDisabledAppearanceSignal;
|
|
114
80
|
private describeActionableItem;
|
|
115
81
|
private semanticText;
|
|
82
|
+
private selectRepresentativeItems;
|
|
83
|
+
private representativeActionStyle;
|
|
84
|
+
private pickRepresentativeItem;
|
|
85
|
+
private representativePriority;
|
|
116
86
|
private isAddToCartItem;
|
|
117
87
|
private isCheckoutItem;
|
|
118
88
|
private isRemoveItemAction;
|
|
@@ -148,4 +118,4 @@ export declare class PageAnalyzer {
|
|
|
148
118
|
private detectPasswordRequirements;
|
|
149
119
|
private generatePasswordVariants;
|
|
150
120
|
}
|
|
151
|
-
//# sourceMappingURL=
|
|
121
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzer/page-analyzer/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { PlaywrightAction, ExpectationType, ExpectationSeverity, } from "@sudobility/testomniac_types";
|
|
2
|
-
import { computeHashes } from "
|
|
2
|
+
import { computeHashes } from "../../browser/page-utils";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { fillValuePlanner } from "
|
|
5
|
-
import { AUTH_URL_PATTERNS, SIGNUP_URL_PATTERNS } from "
|
|
4
|
+
import { fillValuePlanner } from "../../planners/fill-value-planner";
|
|
5
|
+
import { AUTH_URL_PATTERNS, SIGNUP_URL_PATTERNS } from "../../config/constants";
|
|
6
|
+
import { generateHoverFollowUpCases } from "./generators/hover-follow-up";
|
|
7
|
+
import { generateNavigationTestElements } from "./generators/navigation";
|
|
8
|
+
import { generateScaffoldTestElements } from "./generators/scaffolds";
|
|
9
|
+
import { generateRenderTestElements } from "./generators/render";
|
|
10
|
+
import { generateFormTestElements } from "./generators/forms";
|
|
11
|
+
import { generateE2ETestElements } from "./generators/e2e";
|
|
12
|
+
import { generateSemanticJourneyTestElements } from "./generators/semantic-journeys";
|
|
13
|
+
import { generateDialogLifecycleTestElements } from "./generators/dialogs";
|
|
14
|
+
import { generateKeyboardAndDisclosureTestElements } from "./generators/keyboard-disclosure";
|
|
15
|
+
import { generateVariantTestElements } from "./generators/variants";
|
|
16
|
+
import { generateContentTestElements } from "./generators/content";
|
|
6
17
|
/**
|
|
7
18
|
* PageAnalyzer generates expectations and discovers new test elements
|
|
8
19
|
* during discovery mode.
|
|
@@ -51,401 +62,28 @@ export class PageAnalyzer {
|
|
|
51
62
|
currentPageStateId,
|
|
52
63
|
};
|
|
53
64
|
if (this.isHoverOnly(testElement)) {
|
|
54
|
-
await
|
|
65
|
+
await generateHoverFollowUpCases(this, testElement, resolvedContext);
|
|
55
66
|
return;
|
|
56
67
|
}
|
|
57
|
-
|
|
58
|
-
await this
|
|
59
|
-
|
|
60
|
-
await this
|
|
61
|
-
|
|
62
|
-
await this
|
|
63
|
-
|
|
64
|
-
await this
|
|
65
|
-
|
|
66
|
-
await this
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// h. Content test elements — for non-scaffold actionable elements
|
|
72
|
-
await this.generateContentTestElements(resolvedContext);
|
|
73
|
-
// i. Keyboard/disclosure cases
|
|
74
|
-
await this.generateKeyboardAndDisclosureTestElements(resolvedContext);
|
|
75
|
-
// j. Variant/option cases
|
|
76
|
-
await this.generateVariantTestElements(resolvedContext);
|
|
77
|
-
}
|
|
78
|
-
async generateHoverFollowUpCases(testElement, context) {
|
|
79
|
-
const selector = this.getPrimarySelector(testElement);
|
|
80
|
-
if (!selector || !context.currentPageStateId)
|
|
81
|
-
return;
|
|
82
|
-
const beginningItems = context.beginningPageStateId > 0
|
|
83
|
-
? await context.api.getItemsByPageState(context.beginningPageStateId)
|
|
84
|
-
: [];
|
|
85
|
-
const beginningKeys = new Set(beginningItems.map(item => this.getItemKey(item)).filter(Boolean));
|
|
86
|
-
const revealedItems = context.actionableItems.filter(item => {
|
|
87
|
-
if (!this.isMouseActionable(item))
|
|
68
|
+
await generateNavigationTestElements(this, resolvedContext);
|
|
69
|
+
await generateRenderTestElements(this, resolvedContext);
|
|
70
|
+
await generateFormTestElements(this, resolvedContext);
|
|
71
|
+
await generateSemanticJourneyTestElements(this, resolvedContext);
|
|
72
|
+
await generateE2ETestElements(this, resolvedContext);
|
|
73
|
+
await generateDialogLifecycleTestElements(this, resolvedContext);
|
|
74
|
+
await generateScaffoldTestElements(this, resolvedContext);
|
|
75
|
+
await generateContentTestElements(this, resolvedContext);
|
|
76
|
+
await generateKeyboardAndDisclosureTestElements(this, resolvedContext);
|
|
77
|
+
await generateVariantTestElements(this, resolvedContext);
|
|
78
|
+
}
|
|
79
|
+
getScaffoldSurfaceItems(context, scaffold) {
|
|
80
|
+
return context.actionableItems.filter(item => {
|
|
81
|
+
if (!this.isSurfaceCandidate(item) || !item.selector)
|
|
88
82
|
return false;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
const hoveredItem = context.actionableItems.find(item => item.selector === selector) ?? null;
|
|
93
|
-
if (revealedItems.length === 0 && hoveredItem) {
|
|
94
|
-
const clickCase = this.buildClickTestElement(hoveredItem, context.currentPath, context.sizeClass, context.uid, context.currentPageStateId, context.currentTestElementId);
|
|
95
|
-
const tc = await context.api.ensureTestElement(context.runnerId, context.currentTestSurfaceId, clickCase);
|
|
96
|
-
await context.api.createTestElementRun({
|
|
97
|
-
testElementId: tc.id,
|
|
98
|
-
testSurfaceRunId: context.currentSurfaceRunId ?? undefined,
|
|
99
|
-
});
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
for (const item of revealedItems) {
|
|
103
|
-
const nextHover = this.buildHoverTestElement(item, context.currentPath, context.sizeClass, context.uid, context.currentPageStateId, context.currentTestElementId);
|
|
104
|
-
const tc = await context.api.ensureTestElement(context.runnerId, context.currentTestSurfaceId, nextHover);
|
|
105
|
-
await context.api.createTestElementRun({
|
|
106
|
-
testElementId: tc.id,
|
|
107
|
-
testSurfaceRunId: context.currentSurfaceRunId ?? undefined,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
async generateNavigationTestElements(context) {
|
|
112
|
-
if (context.pageRequiresLogin)
|
|
113
|
-
return;
|
|
114
|
-
const { api, runnerId, sizeClass, uid, navigationSurface, bundleRun } = context;
|
|
115
|
-
const links = context.actionableItems.filter(item => item.actionKind === "navigate" && item.href && item.visible);
|
|
116
|
-
// Ensure navigation surface is in the bundle
|
|
117
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, navigationSurface.id);
|
|
118
|
-
// Ensure a surface run exists for the navigation surface under this bundle run
|
|
119
|
-
const surfaceRun = await this.ensureSurfaceRun(api, navigationSurface.id, bundleRun.id);
|
|
120
|
-
for (const link of links) {
|
|
121
|
-
if (!link.href)
|
|
122
|
-
continue;
|
|
123
|
-
const path = this.extractRelativePath(link.href);
|
|
124
|
-
if (!path)
|
|
125
|
-
continue;
|
|
126
|
-
const testElement = this.buildNavigationTestElement(path, sizeClass, uid, context.currentPageStateId);
|
|
127
|
-
const tc = await api.ensureTestElement(runnerId, navigationSurface.id, testElement);
|
|
128
|
-
await api.createTestElementRun({
|
|
129
|
-
testElementId: tc.id,
|
|
130
|
-
testSurfaceRunId: surfaceRun.id,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async generateScaffoldTestElements(context) {
|
|
135
|
-
const { api, runnerId, sizeClass, uid, bundleRun } = context;
|
|
136
|
-
for (const scaffold of context.scaffolds) {
|
|
137
|
-
// Find actionable items belonging to this scaffold
|
|
138
|
-
const scaffoldItems = context.actionableItems.filter(item => item.scaffoldId != null && this.isSurfaceCandidate(item));
|
|
139
|
-
if (scaffoldItems.length === 0)
|
|
140
|
-
continue;
|
|
141
|
-
// Ensure a test surface for this scaffold
|
|
142
|
-
const surfaceTitle = `Scaffold: ${scaffold.type}`;
|
|
143
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
144
|
-
title: surfaceTitle,
|
|
145
|
-
description: `Tests for ${scaffold.type} scaffold`,
|
|
146
|
-
startingPageStateId: context.currentPageStateId,
|
|
147
|
-
startingPath: context.currentPath,
|
|
148
|
-
sizeClass,
|
|
149
|
-
priority: 3,
|
|
150
|
-
surface_tags: ["scaffold", scaffold.type],
|
|
151
|
-
uid,
|
|
152
|
-
});
|
|
153
|
-
context.events.onTestSurfaceCreated({
|
|
154
|
-
surfaceId: surface.id,
|
|
155
|
-
title: surface.title,
|
|
156
|
-
});
|
|
157
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
158
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
159
|
-
for (const item of scaffoldItems) {
|
|
160
|
-
const testElement = this.shouldUseDirectControlInteraction(item)
|
|
161
|
-
? this.buildControlInteractionTestElement(item, context.currentPath, sizeClass, uid, context.currentPageStateId)
|
|
162
|
-
: this.buildHoverTestElement(item, context.currentPath, sizeClass, uid, context.currentPageStateId);
|
|
163
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, testElement);
|
|
164
|
-
await api.createTestElementRun({
|
|
165
|
-
testElementId: tc.id,
|
|
166
|
-
testSurfaceRunId: surfaceRun.id,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
async generateRenderTestElements(context) {
|
|
172
|
-
const { api, runnerId, sizeClass, uid, bundleRun } = context;
|
|
173
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
174
|
-
title: `Render: ${context.currentPath}`,
|
|
175
|
-
description: `Render validation for ${context.currentPath}`,
|
|
176
|
-
startingPageStateId: context.currentPageStateId,
|
|
177
|
-
startingPath: context.currentPath,
|
|
178
|
-
sizeClass,
|
|
179
|
-
priority: 2,
|
|
180
|
-
surface_tags: ["render"],
|
|
181
|
-
uid,
|
|
182
|
-
});
|
|
183
|
-
context.events.onTestSurfaceCreated({
|
|
184
|
-
surfaceId: surface.id,
|
|
185
|
-
title: surface.title,
|
|
186
|
-
});
|
|
187
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
188
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
189
|
-
const testElement = this.buildRenderTestElement(context.currentPath, sizeClass, uid, context.currentPageStateId, context.pageId);
|
|
190
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, testElement);
|
|
191
|
-
await api.createTestElementRun({
|
|
192
|
-
testElementId: tc.id,
|
|
193
|
-
testSurfaceRunId: surfaceRun.id,
|
|
83
|
+
return (context.scaffoldSelectorByItemSelector[item.selector] ===
|
|
84
|
+
scaffold.selector);
|
|
194
85
|
});
|
|
195
86
|
}
|
|
196
|
-
async generateFormTestElements(context) {
|
|
197
|
-
if (context.forms.length === 0)
|
|
198
|
-
return;
|
|
199
|
-
const { api, runnerId, sizeClass, uid, bundleRun } = context;
|
|
200
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
201
|
-
title: `Forms: ${context.currentPath}`,
|
|
202
|
-
description: `Form workflows for ${context.currentPath}`,
|
|
203
|
-
startingPageStateId: context.currentPageStateId,
|
|
204
|
-
startingPath: context.currentPath,
|
|
205
|
-
sizeClass,
|
|
206
|
-
priority: 2,
|
|
207
|
-
surface_tags: ["form"],
|
|
208
|
-
uid,
|
|
209
|
-
});
|
|
210
|
-
context.events.onTestSurfaceCreated({
|
|
211
|
-
surfaceId: surface.id,
|
|
212
|
-
title: surface.title,
|
|
213
|
-
});
|
|
214
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
215
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
216
|
-
for (let index = 0; index < context.forms.length; index++) {
|
|
217
|
-
const form = context.forms[index];
|
|
218
|
-
const formType = this.identifyFormType(form, context.currentPath);
|
|
219
|
-
const formLabel = this.describeForm(form, index);
|
|
220
|
-
const validValues = this.planFormValues(form, context.actionableItems);
|
|
221
|
-
if (this.isSearchForm(form)) {
|
|
222
|
-
const searchTests = this.buildSearchTestElements(form, formLabel, context.currentPath, sizeClass, uid, context.currentPageStateId, validValues, context.actionableItems);
|
|
223
|
-
for (const searchTest of searchTests) {
|
|
224
|
-
const searchElement = await api.ensureTestElement(runnerId, surface.id, searchTest);
|
|
225
|
-
await api.createTestElementRun({
|
|
226
|
-
testElementId: searchElement.id,
|
|
227
|
-
testSurfaceRunId: surfaceRun.id,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const positive = this.buildFormTestElement(form, formLabel, formType, context.currentPath, sizeClass, uid, context.currentPageStateId, validValues);
|
|
233
|
-
const positiveElement = await api.ensureTestElement(runnerId, surface.id, positive);
|
|
234
|
-
await api.createTestElementRun({
|
|
235
|
-
testElementId: positiveElement.id,
|
|
236
|
-
testSurfaceRunId: surfaceRun.id,
|
|
237
|
-
});
|
|
238
|
-
for (const field of form.fields.filter(field => this.isNegativeCandidateField(field))) {
|
|
239
|
-
const negative = this.buildNegativeFormTestElement(form, formLabel, formType, field, context.currentPath, sizeClass, uid, context.currentPageStateId, validValues);
|
|
240
|
-
const negativeElement = await api.ensureTestElement(runnerId, surface.id, negative);
|
|
241
|
-
await api.createTestElementRun({
|
|
242
|
-
testElementId: negativeElement.id,
|
|
243
|
-
testSurfaceRunId: surfaceRun.id,
|
|
244
|
-
});
|
|
245
|
-
const correction = this.buildFormCorrectionTestElement(form, formLabel, formType, field, context.currentPath, sizeClass, uid, context.currentPageStateId, validValues);
|
|
246
|
-
const correctionElement = await api.ensureTestElement(runnerId, surface.id, correction);
|
|
247
|
-
await api.createTestElementRun({
|
|
248
|
-
testElementId: correctionElement.id,
|
|
249
|
-
testSurfaceRunId: surfaceRun.id,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
if (this.isPasswordScenario(formType, form)) {
|
|
253
|
-
const passwordTests = this.buildPasswordTestElements(form, formLabel, formType, context.currentPath, sizeClass, uid, context.currentPageStateId, validValues, this.detectPasswordRequirements(this.extractVisibleText(context.html)));
|
|
254
|
-
for (const passwordTest of passwordTests) {
|
|
255
|
-
const passwordElement = await api.ensureTestElement(runnerId, surface.id, passwordTest);
|
|
256
|
-
await api.createTestElementRun({
|
|
257
|
-
testElementId: passwordElement.id,
|
|
258
|
-
testSurfaceRunId: surfaceRun.id,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
async generateE2ETestElements(context) {
|
|
265
|
-
if (context.journeySteps.length < 2)
|
|
266
|
-
return;
|
|
267
|
-
const { api, runnerId, sizeClass, uid, bundleRun } = context;
|
|
268
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
269
|
-
title: `Journeys: ${context.currentPath}`,
|
|
270
|
-
description: `End-to-end journeys reaching ${context.currentPath}`,
|
|
271
|
-
startingPageStateId: context.currentPageStateId,
|
|
272
|
-
startingPath: context.currentPath,
|
|
273
|
-
sizeClass,
|
|
274
|
-
priority: 2,
|
|
275
|
-
surface_tags: ["e2e"],
|
|
276
|
-
uid,
|
|
277
|
-
});
|
|
278
|
-
context.events.onTestSurfaceCreated({
|
|
279
|
-
surfaceId: surface.id,
|
|
280
|
-
title: surface.title,
|
|
281
|
-
});
|
|
282
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
283
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
284
|
-
const e2e = this.buildE2ETestElement(context.currentPath, sizeClass, uid, context.currentPageStateId, context.journeySteps);
|
|
285
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, e2e);
|
|
286
|
-
await api.createTestElementRun({
|
|
287
|
-
testElementId: tc.id,
|
|
288
|
-
testSurfaceRunId: surfaceRun.id,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
async generateSemanticJourneyTestElements(context) {
|
|
292
|
-
const journeys = this.buildSemanticJourneyTestElements(context);
|
|
293
|
-
if (journeys.length === 0)
|
|
294
|
-
return;
|
|
295
|
-
const { api, runnerId, bundleRun } = context;
|
|
296
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
297
|
-
title: `Journeys: ${context.currentPath}`,
|
|
298
|
-
description: `Semantic multi-step journeys from ${context.currentPath}`,
|
|
299
|
-
startingPageStateId: context.currentPageStateId,
|
|
300
|
-
startingPath: context.currentPath,
|
|
301
|
-
sizeClass: context.sizeClass,
|
|
302
|
-
priority: 2,
|
|
303
|
-
surface_tags: ["e2e", "semantic-journey"],
|
|
304
|
-
uid: context.uid,
|
|
305
|
-
});
|
|
306
|
-
context.events.onTestSurfaceCreated({
|
|
307
|
-
surfaceId: surface.id,
|
|
308
|
-
title: surface.title,
|
|
309
|
-
});
|
|
310
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
311
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
312
|
-
for (const journey of journeys) {
|
|
313
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, journey);
|
|
314
|
-
await api.createTestElementRun({
|
|
315
|
-
testElementId: tc.id,
|
|
316
|
-
testSurfaceRunId: surfaceRun.id,
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
async generateDialogLifecycleTestElements(context) {
|
|
321
|
-
if (!this.pageHasOpenDialog(context.html))
|
|
322
|
-
return;
|
|
323
|
-
const closeCandidates = context.actionableItems.filter(item => item.visible &&
|
|
324
|
-
!item.disabled &&
|
|
325
|
-
item.selector &&
|
|
326
|
-
this.isDialogCloseItem(item));
|
|
327
|
-
const tests = [];
|
|
328
|
-
for (const item of closeCandidates) {
|
|
329
|
-
tests.push(this.buildDialogCloseTestElement(item, context.currentPath, context.sizeClass, context.uid, context.currentPageStateId));
|
|
330
|
-
}
|
|
331
|
-
tests.push(this.buildEscapeDialogTestElement(context.currentPath, context.sizeClass, context.uid, context.currentPageStateId));
|
|
332
|
-
const { api, runnerId, bundleRun } = context;
|
|
333
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
334
|
-
title: `Dialogs: ${context.currentPath}`,
|
|
335
|
-
description: `Dialog lifecycle checks for ${context.currentPath}`,
|
|
336
|
-
startingPageStateId: context.currentPageStateId,
|
|
337
|
-
startingPath: context.currentPath,
|
|
338
|
-
sizeClass: context.sizeClass,
|
|
339
|
-
priority: 2,
|
|
340
|
-
surface_tags: ["dialog"],
|
|
341
|
-
uid: context.uid,
|
|
342
|
-
});
|
|
343
|
-
context.events.onTestSurfaceCreated({
|
|
344
|
-
surfaceId: surface.id,
|
|
345
|
-
title: surface.title,
|
|
346
|
-
});
|
|
347
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
348
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
349
|
-
for (const test of tests) {
|
|
350
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, test);
|
|
351
|
-
await api.createTestElementRun({
|
|
352
|
-
testElementId: tc.id,
|
|
353
|
-
testSurfaceRunId: surfaceRun.id,
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
async generateKeyboardAndDisclosureTestElements(context) {
|
|
358
|
-
const tests = this.buildKeyboardAndDisclosureTestElements(context);
|
|
359
|
-
if (tests.length === 0)
|
|
360
|
-
return;
|
|
361
|
-
const { api, runnerId, bundleRun } = context;
|
|
362
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
363
|
-
title: `Keyboard: ${context.currentPath}`,
|
|
364
|
-
description: `Keyboard parity and disclosure checks for ${context.currentPath}`,
|
|
365
|
-
startingPageStateId: context.currentPageStateId,
|
|
366
|
-
startingPath: context.currentPath,
|
|
367
|
-
sizeClass: context.sizeClass,
|
|
368
|
-
priority: 3,
|
|
369
|
-
surface_tags: ["keyboard", "disclosure"],
|
|
370
|
-
uid: context.uid,
|
|
371
|
-
});
|
|
372
|
-
context.events.onTestSurfaceCreated({
|
|
373
|
-
surfaceId: surface.id,
|
|
374
|
-
title: surface.title,
|
|
375
|
-
});
|
|
376
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
377
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
378
|
-
for (const test of tests) {
|
|
379
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, test);
|
|
380
|
-
await api.createTestElementRun({
|
|
381
|
-
testElementId: tc.id,
|
|
382
|
-
testSurfaceRunId: surfaceRun.id,
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
async generateVariantTestElements(context) {
|
|
387
|
-
const tests = this.buildVariantTestElements(context);
|
|
388
|
-
if (tests.length === 0)
|
|
389
|
-
return;
|
|
390
|
-
const { api, runnerId, bundleRun } = context;
|
|
391
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
392
|
-
title: `Variants: ${context.currentPath}`,
|
|
393
|
-
description: `Variant and option state checks for ${context.currentPath}`,
|
|
394
|
-
startingPageStateId: context.currentPageStateId,
|
|
395
|
-
startingPath: context.currentPath,
|
|
396
|
-
sizeClass: context.sizeClass,
|
|
397
|
-
priority: 2,
|
|
398
|
-
surface_tags: ["variant", "option"],
|
|
399
|
-
uid: context.uid,
|
|
400
|
-
});
|
|
401
|
-
context.events.onTestSurfaceCreated({
|
|
402
|
-
surfaceId: surface.id,
|
|
403
|
-
title: surface.title,
|
|
404
|
-
});
|
|
405
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
406
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
407
|
-
for (const test of tests) {
|
|
408
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, test);
|
|
409
|
-
await api.createTestElementRun({
|
|
410
|
-
testElementId: tc.id,
|
|
411
|
-
testSurfaceRunId: surfaceRun.id,
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
async generateContentTestElements(context) {
|
|
416
|
-
const { api, runnerId, sizeClass, uid, bundleRun, pageId: _pageId, } = context;
|
|
417
|
-
// Content items are those NOT in a scaffold
|
|
418
|
-
const contentItems = context.actionableItems.filter(item => item.scaffoldId == null && this.isSurfaceCandidate(item));
|
|
419
|
-
if (contentItems.length === 0)
|
|
420
|
-
return;
|
|
421
|
-
const surfaceTitle = `Page: ${context.currentPath}`;
|
|
422
|
-
const surface = await api.ensureTestSurface(runnerId, {
|
|
423
|
-
title: surfaceTitle,
|
|
424
|
-
description: `Tests for page content at ${context.currentPath}`,
|
|
425
|
-
startingPageStateId: context.currentPageStateId,
|
|
426
|
-
startingPath: context.currentPath,
|
|
427
|
-
sizeClass,
|
|
428
|
-
priority: 3,
|
|
429
|
-
surface_tags: ["page-content"],
|
|
430
|
-
uid,
|
|
431
|
-
});
|
|
432
|
-
context.events.onTestSurfaceCreated({
|
|
433
|
-
surfaceId: surface.id,
|
|
434
|
-
title: surface.title,
|
|
435
|
-
});
|
|
436
|
-
await api.ensureBundleSurfaceLink(bundleRun.testSurfaceBundleId, surface.id);
|
|
437
|
-
const surfaceRun = await this.ensureSurfaceRun(api, surface.id, bundleRun.id);
|
|
438
|
-
for (const item of contentItems) {
|
|
439
|
-
const testElement = this.shouldUseDirectControlInteraction(item)
|
|
440
|
-
? this.buildControlInteractionTestElement(item, context.currentPath, sizeClass, uid, context.currentPageStateId)
|
|
441
|
-
: this.buildHoverTestElement(item, context.currentPath, sizeClass, uid, context.currentPageStateId);
|
|
442
|
-
const tc = await api.ensureTestElement(runnerId, surface.id, testElement);
|
|
443
|
-
await api.createTestElementRun({
|
|
444
|
-
testElementId: tc.id,
|
|
445
|
-
testSurfaceRunId: surfaceRun.id,
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
87
|
async ensureSurfaceRun(api, testSurfaceId, bundleRunId) {
|
|
450
88
|
const openSurfaceRuns = await api.getOpenTestSurfaceRuns(bundleRunId);
|
|
451
89
|
const existing = openSurfaceRuns.find(surfaceRun => surfaceRun.testSurfaceId === testSurfaceId);
|
|
@@ -1245,7 +883,7 @@ export class PageAnalyzer {
|
|
|
1245
883
|
return `${descriptor} @ ${form.selector}`;
|
|
1246
884
|
}
|
|
1247
885
|
buildSemanticJourneyTestElements(context) {
|
|
1248
|
-
const items = context.actionableItems.filter(item => item.visible && !item.disabled && Boolean(item.selector));
|
|
886
|
+
const items = this.selectRepresentativeItems(context.actionableItems.filter(item => item.visible && !item.disabled && Boolean(item.selector)));
|
|
1249
887
|
const journeys = [];
|
|
1250
888
|
const collectionCount = this.estimateCollectionCount(context.html);
|
|
1251
889
|
const addToCart = items.find(item => this.isAddToCartItem(item));
|
|
@@ -1660,7 +1298,7 @@ export class PageAnalyzer {
|
|
|
1660
1298
|
return field.type === "checkbox" || field.type === "radio";
|
|
1661
1299
|
}
|
|
1662
1300
|
buildKeyboardAndDisclosureTestElements(context) {
|
|
1663
|
-
const items = context.actionableItems.filter(item => item.visible && !item.disabled && Boolean(item.selector));
|
|
1301
|
+
const items = this.selectRepresentativeItems(context.actionableItems.filter(item => item.visible && !item.disabled && Boolean(item.selector)));
|
|
1664
1302
|
const tests = [];
|
|
1665
1303
|
for (const item of items) {
|
|
1666
1304
|
if (this.isDisclosureItem(item)) {
|
|
@@ -1693,10 +1331,10 @@ export class PageAnalyzer {
|
|
|
1693
1331
|
return tests;
|
|
1694
1332
|
}
|
|
1695
1333
|
buildVariantTestElements(context) {
|
|
1696
|
-
const items = context.actionableItems.filter(item => item.visible &&
|
|
1334
|
+
const items = this.selectRepresentativeItems(context.actionableItems.filter(item => item.visible &&
|
|
1697
1335
|
!item.disabled &&
|
|
1698
1336
|
Boolean(item.selector) &&
|
|
1699
|
-
this.isVariantSelector(item));
|
|
1337
|
+
this.isVariantSelector(item)));
|
|
1700
1338
|
const tests = items
|
|
1701
1339
|
.map(item => this.buildVariantTestElement(item, context))
|
|
1702
1340
|
.filter((item) => Boolean(item));
|
|
@@ -2178,6 +1816,7 @@ export class PageAnalyzer {
|
|
|
2178
1816
|
describeActionableItem(item) {
|
|
2179
1817
|
return (item.accessibleName ||
|
|
2180
1818
|
item.textContent ||
|
|
1819
|
+
String(item.attributes?._containerTitle ?? "") ||
|
|
2181
1820
|
String(item.attributes?.labelText ?? "") ||
|
|
2182
1821
|
String(item.attributes?.placeholder ?? "") ||
|
|
2183
1822
|
item.selector);
|
|
@@ -2190,12 +1829,93 @@ export class PageAnalyzer {
|
|
|
2190
1829
|
String(item.attributes?.placeholder ?? ""),
|
|
2191
1830
|
String(item.attributes?.name ?? ""),
|
|
2192
1831
|
String(item.attributes?.id ?? ""),
|
|
1832
|
+
String(item.attributes?._containerTitle ?? ""),
|
|
1833
|
+
String(item.attributes?._containerCtaStyle ?? ""),
|
|
2193
1834
|
item.href || "",
|
|
2194
1835
|
item.selector,
|
|
2195
1836
|
]
|
|
2196
1837
|
.join(" ")
|
|
2197
1838
|
.toLowerCase();
|
|
2198
1839
|
}
|
|
1840
|
+
selectRepresentativeItems(items) {
|
|
1841
|
+
const groups = new Map();
|
|
1842
|
+
const passthrough = [];
|
|
1843
|
+
for (const item of items) {
|
|
1844
|
+
const containerFingerprint = String(item.attributes?._containerFingerprint ?? "").trim();
|
|
1845
|
+
if (!containerFingerprint) {
|
|
1846
|
+
passthrough.push(item);
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1849
|
+
const key = `${containerFingerprint}|${this.representativeActionStyle(item)}`;
|
|
1850
|
+
const bucket = groups.get(key) ?? [];
|
|
1851
|
+
bucket.push(item);
|
|
1852
|
+
groups.set(key, bucket);
|
|
1853
|
+
}
|
|
1854
|
+
const representatives = Array.from(groups.values()).map(group => this.pickRepresentativeItem(group));
|
|
1855
|
+
return [...passthrough, ...representatives];
|
|
1856
|
+
}
|
|
1857
|
+
representativeActionStyle(item) {
|
|
1858
|
+
const text = this.semanticText(item);
|
|
1859
|
+
const sourceHints = String(item.attributes?._sourceHints ?? "").toLowerCase();
|
|
1860
|
+
const containerTitle = String(item.attributes?._containerTitle ?? "").toLowerCase();
|
|
1861
|
+
if (sourceHints.includes("promoted-target") ||
|
|
1862
|
+
sourceHints.includes("cursor-pointer")) {
|
|
1863
|
+
return "tile-click";
|
|
1864
|
+
}
|
|
1865
|
+
if (item.actionKind === "navigate" &&
|
|
1866
|
+
containerTitle &&
|
|
1867
|
+
(text.includes(containerTitle) ||
|
|
1868
|
+
(item.href && !this.isCheckoutItem(item)))) {
|
|
1869
|
+
return "tile-click";
|
|
1870
|
+
}
|
|
1871
|
+
if (this.isAddToCartItem(item))
|
|
1872
|
+
return "cta:add-to-cart";
|
|
1873
|
+
if (/\blogin for pricing\b/.test(text))
|
|
1874
|
+
return "cta:login-for-pricing";
|
|
1875
|
+
if (/\bselect options\b/.test(text))
|
|
1876
|
+
return "cta:select-options";
|
|
1877
|
+
if (this.isCheckoutItem(item))
|
|
1878
|
+
return "cta:checkout";
|
|
1879
|
+
if (this.isRemoveItemAction(item))
|
|
1880
|
+
return "cta:remove";
|
|
1881
|
+
if (item.actionKind === "navigate")
|
|
1882
|
+
return "navigate";
|
|
1883
|
+
if (item.actionKind === "select")
|
|
1884
|
+
return "select";
|
|
1885
|
+
if (item.actionKind === "fill")
|
|
1886
|
+
return "fill";
|
|
1887
|
+
return `${item.actionKind}:${text
|
|
1888
|
+
.replace(/\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/g, "email")
|
|
1889
|
+
.replace(/\$\s?\d[\d,.]*/g, "price")
|
|
1890
|
+
.replace(/\d+/g, "n")
|
|
1891
|
+
.slice(0, 80)}`;
|
|
1892
|
+
}
|
|
1893
|
+
pickRepresentativeItem(items) {
|
|
1894
|
+
return [...items].sort((left, right) => {
|
|
1895
|
+
const leftScore = this.representativePriority(left);
|
|
1896
|
+
const rightScore = this.representativePriority(right);
|
|
1897
|
+
if (leftScore !== rightScore)
|
|
1898
|
+
return rightScore - leftScore;
|
|
1899
|
+
return (left.selector?.length ?? 0) - (right.selector?.length ?? 0);
|
|
1900
|
+
})[0];
|
|
1901
|
+
}
|
|
1902
|
+
representativePriority(item) {
|
|
1903
|
+
let score = 0;
|
|
1904
|
+
const sourceHints = String(item.attributes?._sourceHints ?? "").toLowerCase();
|
|
1905
|
+
if (sourceHints.includes("promoted-target"))
|
|
1906
|
+
score += 4;
|
|
1907
|
+
if (sourceHints.includes("cursor-pointer"))
|
|
1908
|
+
score += 3;
|
|
1909
|
+
if (item.actionKind === "navigate")
|
|
1910
|
+
score += 2;
|
|
1911
|
+
if (item.actionKind === "click")
|
|
1912
|
+
score += 1;
|
|
1913
|
+
if (String(item.attributes?._testId ?? ""))
|
|
1914
|
+
score += 1;
|
|
1915
|
+
if (item.accessibleName)
|
|
1916
|
+
score += 1;
|
|
1917
|
+
return score;
|
|
1918
|
+
}
|
|
2199
1919
|
isAddToCartItem(item) {
|
|
2200
1920
|
return /\b(add to cart|add to bag|buy now|add item)\b/.test(this.semanticText(item));
|
|
2201
1921
|
}
|
|
@@ -2461,4 +2181,4 @@ export class PageAnalyzer {
|
|
|
2461
2181
|
return variants;
|
|
2462
2182
|
}
|
|
2463
2183
|
}
|
|
2464
|
-
//# sourceMappingURL=
|
|
2184
|
+
//# sourceMappingURL=index.js.map
|