@sudobility/testomniac_runner_service 0.1.89 → 0.1.91
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/index.d.ts +8 -0
- package/dist/analyzer/page-analyzer/index.d.ts.map +1 -1
- package/dist/analyzer/page-analyzer/index.js +64 -31
- package/dist/analyzer/page-analyzer/index.js.map +1 -1
- package/dist/browser/page-utils.d.ts +10 -0
- package/dist/browser/page-utils.d.ts.map +1 -1
- package/dist/browser/page-utils.js +74 -2
- package/dist/browser/page-utils.js.map +1 -1
- package/dist/orchestrator/runner.d.ts.map +1 -1
- package/dist/orchestrator/runner.js +64 -30
- package/dist/orchestrator/runner.js.map +1 -1
- package/dist/orchestrator/test-interaction-executor.d.ts +2 -2
- package/dist/orchestrator/test-interaction-executor.d.ts.map +1 -1
- package/dist/orchestrator/test-interaction-executor.js +3 -2
- package/dist/orchestrator/test-interaction-executor.js.map +1 -1
- package/package.json +1 -1
|
@@ -172,6 +172,14 @@ export declare class PageAnalyzer {
|
|
|
172
172
|
private hasDisabledAppearanceSignal;
|
|
173
173
|
private describeActionableItem;
|
|
174
174
|
private semanticText;
|
|
175
|
+
/**
|
|
176
|
+
* Max representatives per action style. Product grids can have dozens of
|
|
177
|
+
* cards each producing a unique container fingerprint (because the product
|
|
178
|
+
* title differs), but the CTA buttons ("ADD TO CART", "Select Options") are
|
|
179
|
+
* functionally identical. Capping per style prevents 14× duplicate hover
|
|
180
|
+
* tests for the same button type across different cards.
|
|
181
|
+
*/
|
|
182
|
+
private static readonly MAX_REPS_PER_STYLE;
|
|
175
183
|
private selectRepresentativeItems;
|
|
176
184
|
private representativeActionStyle;
|
|
177
185
|
private pickRepresentativeItem;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzer/page-analyzer/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EAQZ,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzer/page-analyzer/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PlaywrightAction, ExpectationType, ExpectationSeverity, } from "@sudobility/testomniac_types";
|
|
2
2
|
import { buildReplaySelectorFromActionableItem, matchesActionableItemSelector, } from "../../browser/replay-selector";
|
|
3
|
-
import { computeHashes, computeActionableHash, sha256, normalizeHtml, } from "../../browser/page-utils";
|
|
3
|
+
import { computeHashes, computeActionableHash, sha256, normalizeHtml, htmlToMarkdown, } from "../../browser/page-utils";
|
|
4
4
|
import { getBody, getContentBody } from "../../scanner/html-decomposer";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { fillValuePlanner } from "../../planners/fill-value-planner";
|
|
@@ -260,6 +260,32 @@ export class PageAnalyzer {
|
|
|
260
260
|
return;
|
|
261
261
|
}
|
|
262
262
|
const normalizedContext = this.normalizeContext(context);
|
|
263
|
+
const isHover = this.isHoverOnly(testInteraction);
|
|
264
|
+
const currentPath = normalizedContext.currentPath.trim();
|
|
265
|
+
// For non-hover interactions: check cheap path/hash guards BEFORE the
|
|
266
|
+
// expensive ensureTargetPageState call so we can skip all the API work
|
|
267
|
+
// (scaffold resolution, hash computation, page-state creation) when the
|
|
268
|
+
// page has already been covered in this run.
|
|
269
|
+
if (!isHover) {
|
|
270
|
+
if (this.generatedPaths.has(currentPath)) {
|
|
271
|
+
logAnalyzer("generate:page-already-covered", {
|
|
272
|
+
sourceTitle: testInteraction.title,
|
|
273
|
+
currentTestInteractionId: normalizedContext.currentTestInteractionId,
|
|
274
|
+
currentPath,
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const actionableHash = await computeActionableHash(normalizedContext.actionableItems);
|
|
279
|
+
if (this.generatedActionableHashes.has(actionableHash)) {
|
|
280
|
+
logAnalyzer("generate:actionable-items-already-covered", {
|
|
281
|
+
sourceTitle: testInteraction.title,
|
|
282
|
+
currentTestInteractionId: normalizedContext.currentTestInteractionId,
|
|
283
|
+
currentPath,
|
|
284
|
+
actionableHash,
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
263
289
|
const currentPageStateId = await this.ensureTargetPageState(normalizedContext);
|
|
264
290
|
const resolvedContext = {
|
|
265
291
|
...normalizedContext,
|
|
@@ -278,7 +304,7 @@ export class PageAnalyzer {
|
|
|
278
304
|
// Hover-only interactions have their own same-page-state handling inside
|
|
279
305
|
// generateHoverFollowUpCases (including click-follow-up reconciliation),
|
|
280
306
|
// so let them through to that dedicated path.
|
|
281
|
-
if (
|
|
307
|
+
if (isHover) {
|
|
282
308
|
logAnalyzer("generate:hover-only", {
|
|
283
309
|
sourceTitle: testInteraction.title,
|
|
284
310
|
currentTestInteractionId: resolvedContext.currentTestInteractionId,
|
|
@@ -300,34 +326,9 @@ export class PageAnalyzer {
|
|
|
300
326
|
});
|
|
301
327
|
return;
|
|
302
328
|
}
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
// previous runs on the same runner don't block re-discovery.
|
|
306
|
-
const currentPath = resolvedContext.currentPath.trim();
|
|
307
|
-
if (this.generatedPaths.has(currentPath)) {
|
|
308
|
-
logAnalyzer("generate:page-already-covered", {
|
|
309
|
-
sourceTitle: testInteraction.title,
|
|
310
|
-
currentTestInteractionId: resolvedContext.currentTestInteractionId,
|
|
311
|
-
currentPageStateId: resolvedContext.currentPageStateId,
|
|
312
|
-
currentPath,
|
|
313
|
-
});
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
// Skip generation if a different path already produced the same set of
|
|
317
|
-
// interactive elements. For example /store/ and /store/all-items/ may
|
|
318
|
-
// resolve to the same product grid — generating hover/click tests for
|
|
319
|
-
// both is redundant.
|
|
329
|
+
// Re-check actionableHash (already passed the guard above, but we need
|
|
330
|
+
// to record it and the path now that we're committed to generation).
|
|
320
331
|
const actionableHash = await computeActionableHash(resolvedContext.actionableItems);
|
|
321
|
-
if (this.generatedActionableHashes.has(actionableHash)) {
|
|
322
|
-
logAnalyzer("generate:actionable-items-already-covered", {
|
|
323
|
-
sourceTitle: testInteraction.title,
|
|
324
|
-
currentTestInteractionId: resolvedContext.currentTestInteractionId,
|
|
325
|
-
currentPageStateId: resolvedContext.currentPageStateId,
|
|
326
|
-
currentPath,
|
|
327
|
-
actionableHash,
|
|
328
|
-
});
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
332
|
this.generatedPaths.add(currentPath);
|
|
332
333
|
this.generatedActionableHashes.add(actionableHash);
|
|
333
334
|
// If a hover+click navigated to a new page, create a direct navigation
|
|
@@ -714,7 +715,7 @@ export class PageAnalyzer {
|
|
|
714
715
|
pageId: context.pageId,
|
|
715
716
|
sizeClass: context.sizeClass,
|
|
716
717
|
hashes,
|
|
717
|
-
contentText: context.html.slice(0, 5000),
|
|
718
|
+
contentText: htmlToMarkdown(context.html).slice(0, 5000),
|
|
718
719
|
contentHtmlElementId: contentElement.id,
|
|
719
720
|
screenshotPath: context.screenshotPath,
|
|
720
721
|
});
|
|
@@ -2384,6 +2385,14 @@ export class PageAnalyzer {
|
|
|
2384
2385
|
.join(" ")
|
|
2385
2386
|
.toLowerCase();
|
|
2386
2387
|
}
|
|
2388
|
+
/**
|
|
2389
|
+
* Max representatives per action style. Product grids can have dozens of
|
|
2390
|
+
* cards each producing a unique container fingerprint (because the product
|
|
2391
|
+
* title differs), but the CTA buttons ("ADD TO CART", "Select Options") are
|
|
2392
|
+
* functionally identical. Capping per style prevents 14× duplicate hover
|
|
2393
|
+
* tests for the same button type across different cards.
|
|
2394
|
+
*/
|
|
2395
|
+
static MAX_REPS_PER_STYLE = 2;
|
|
2387
2396
|
selectRepresentativeItems(items) {
|
|
2388
2397
|
const groups = new Map();
|
|
2389
2398
|
const passthrough = [];
|
|
@@ -2399,7 +2408,31 @@ export class PageAnalyzer {
|
|
|
2399
2408
|
groups.set(key, bucket);
|
|
2400
2409
|
}
|
|
2401
2410
|
const representatives = Array.from(groups.values()).map(group => this.pickRepresentativeItem(group));
|
|
2402
|
-
|
|
2411
|
+
// Cap per action style: when many different containers share the same
|
|
2412
|
+
// functional action (e.g. 14 product cards each with "ADD TO CART"),
|
|
2413
|
+
// keep at most MAX_REPS_PER_STYLE representatives per style.
|
|
2414
|
+
const byStyle = new Map();
|
|
2415
|
+
for (const rep of representatives) {
|
|
2416
|
+
const style = this.representativeActionStyle(rep);
|
|
2417
|
+
const bucket = byStyle.get(style) ?? [];
|
|
2418
|
+
bucket.push(rep);
|
|
2419
|
+
byStyle.set(style, bucket);
|
|
2420
|
+
}
|
|
2421
|
+
const capped = Array.from(byStyle.values()).flatMap(group => group.length <= PageAnalyzer.MAX_REPS_PER_STYLE
|
|
2422
|
+
? group
|
|
2423
|
+
: group.slice(0, PageAnalyzer.MAX_REPS_PER_STYLE));
|
|
2424
|
+
// Also cap passthrough items (no container fingerprint) by action style
|
|
2425
|
+
const passthroughByStyle = new Map();
|
|
2426
|
+
for (const item of passthrough) {
|
|
2427
|
+
const style = this.representativeActionStyle(item);
|
|
2428
|
+
const bucket = passthroughByStyle.get(style) ?? [];
|
|
2429
|
+
bucket.push(item);
|
|
2430
|
+
passthroughByStyle.set(style, bucket);
|
|
2431
|
+
}
|
|
2432
|
+
const cappedPassthrough = Array.from(passthroughByStyle.values()).flatMap(group => group.length <= PageAnalyzer.MAX_REPS_PER_STYLE
|
|
2433
|
+
? group
|
|
2434
|
+
: group.slice(0, PageAnalyzer.MAX_REPS_PER_STYLE));
|
|
2435
|
+
return [...cappedPassthrough, ...capped];
|
|
2403
2436
|
}
|
|
2404
2437
|
representativeActionStyle(item) {
|
|
2405
2438
|
const text = this.semanticText(item);
|