Skip to content

Commit 71c0c04

Browse files
feat: staktrak journey pass-fail (#4633)
* feat: staktrak journey pass-fail * fix: playwright recording manager --------- Co-authored-by: Evan Feenstra <evanfeenstra@gmail.com>
1 parent 2ca32cd commit 71c0c04

5 files changed

Lines changed: 354 additions & 62 deletions

File tree

public/js/playwright-generator.js

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,36 @@ var PlaywrightGenerator = (() => {
208208
}
209209

210210
// src/utils.ts
211+
var filterClickDetails = (clickDetails, assertions, config) => {
212+
if (!clickDetails.length)
213+
return [];
214+
let filtered = config.filterAssertionClicks ? clickDetails.filter(
215+
(click) => !assertions.some(
216+
(assertion) => Math.abs(click.timestamp - assertion.timestamp) < 1e3 && (click.selectors.primary.includes(assertion.selector) || assertion.selector.includes(click.selectors.primary) || click.selectors.fallbacks.some(
217+
(f) => f.includes(assertion.selector) || assertion.selector.includes(f)
218+
))
219+
)
220+
) : clickDetails;
221+
const clicksBySelector = {};
222+
filtered.forEach((click) => {
223+
const key = click.selectors.primary;
224+
if (!clicksBySelector[key])
225+
clicksBySelector[key] = [];
226+
clicksBySelector[key].push(click);
227+
});
228+
const result = [];
229+
Object.values(clicksBySelector).forEach((clicks) => {
230+
clicks.sort((a, b) => a.timestamp - b.timestamp);
231+
let lastClick = null;
232+
clicks.forEach((click) => {
233+
if (!lastClick || click.timestamp - lastClick.timestamp > config.multiClickInterval) {
234+
result.push(click);
235+
}
236+
lastClick = click;
237+
});
238+
});
239+
return result.sort((a, b) => a.timestamp - b.timestamp);
240+
};
211241
function getRelativeUrl(url) {
212242
if (!url)
213243
return "/";
@@ -242,6 +272,10 @@ var PlaywrightGenerator = (() => {
242272
}
243273

244274
// src/playwright-generator.ts
275+
var CLICK_FILTER_CONFIG = {
276+
filterAssertionClicks: true,
277+
multiClickInterval: 300
278+
};
245279
var RecordingManager = class {
246280
constructor() {
247281
this.trackingData = {
@@ -413,11 +447,32 @@ var PlaywrightGenerator = (() => {
413447
break;
414448
}
415449
}
450+
/**
451+
* trackingData with the same assertion-adjacent click filtering the in-iframe
452+
* processResults() pipeline applies (filterClickDetails in utils.ts) — e.g. the
453+
* click a text-selection drag can spuriously register right before an assertion.
454+
* generateTest()/getReplaySteps() are the ONLY paths ever used for output (the
455+
* host only calls recorder.generateTest()/getReplaySteps(); the filtered
456+
* staktrak-results payload from processResults() is not read for generation — see
457+
* hive useStaktrak.ts and mcp/tests/hooks.js), so without this, that filter never
458+
* actually ran and generated tests could carry an extra bogus click.
459+
*/
460+
filteredTrackingData() {
461+
return __spreadProps(__spreadValues({}, this.trackingData), {
462+
clicks: __spreadProps(__spreadValues({}, this.trackingData.clicks), {
463+
clickDetails: filterClickDetails(
464+
this.trackingData.clicks.clickDetails,
465+
this.trackingData.assertions,
466+
CLICK_FILTER_CONFIG
467+
)
468+
})
469+
});
470+
}
416471
/**
417472
* Generate Playwright test from current data
418473
*/
419474
generateTest(url, options) {
420-
const actions = resultsToActions(this.trackingData);
475+
const actions = resultsToActions(this.filteredTrackingData());
421476
return generatePlaywrightTestFromActions(actions, __spreadValues({
422477
baseUrl: url
423478
}, options));
@@ -427,7 +482,7 @@ var PlaywrightGenerator = (() => {
427482
* directly (no generated text, no re-parse). Single source of truth = trackingData.
428483
*/
429484
getReplaySteps(url) {
430-
const actions = resultsToActions(this.trackingData);
485+
const actions = resultsToActions(this.filteredTrackingData());
431486
return actionsToReplaySteps(actions, { baseUrl: url });
432487
}
433488
/**
@@ -484,7 +539,7 @@ var PlaywrightGenerator = (() => {
484539
}
485540
return null;
486541
}
487-
function clickExpr(locator) {
542+
function locatorExpr(locator) {
488543
const testId = extractTestId(locator);
489544
if (testId)
490545
return `page.getByTestId('${q(testId)}')`;
@@ -494,10 +549,6 @@ var PlaywrightGenerator = (() => {
494549
const [, role, name] = roleM;
495550
return name ? `page.getByRole('${q(role)}', { name: '${q(name)}' })` : `page.getByRole('${q(role)}')`;
496551
}
497-
return cssLocator(locator);
498-
}
499-
function cssLocator(locator) {
500-
const sel = (locator == null ? void 0 : locator.stableSelector) || (locator == null ? void 0 : locator.primary) || "";
501552
return `page.locator('${q(sel)}')`;
502553
}
503554
function generatePlaywrightTestFromActions(actions, options = {}) {
@@ -518,17 +569,17 @@ var PlaywrightGenerator = (() => {
518569
case "click": {
519570
if (!((_a = action.locator) == null ? void 0 : _a.primary) && !((_b = action.locator) == null ? void 0 : _b.stableSelector))
520571
return "";
521-
return ` await ${clickExpr(action.locator)}.click();`;
572+
return ` await ${locatorExpr(action.locator)}.click();`;
522573
}
523574
case "input": {
524575
if (!((_c = action.locator) == null ? void 0 : _c.primary) || action.value === void 0)
525576
return "";
526-
return ` await ${cssLocator(action.locator)}.fill('${q(action.value)}');`;
577+
return ` await ${locatorExpr(action.locator)}.fill('${q(action.value)}');`;
527578
}
528579
case "form": {
529580
if (!((_d = action.locator) == null ? void 0 : _d.primary))
530581
return "";
531-
const target = cssLocator(action.locator);
582+
const target = locatorExpr(action.locator);
532583
if (action.formType === "checkbox" || action.formType === "radio") {
533584
return action.checked ? ` await ${target}.check();` : ` await ${target}.uncheck();`;
534585
} else if (action.formType === "select" && action.value !== void 0) {
@@ -539,7 +590,7 @@ var PlaywrightGenerator = (() => {
539590
case "assertion": {
540591
if (!((_e = action.locator) == null ? void 0 : _e.primary) || action.value === void 0)
541592
return "";
542-
return ` await expect(${cssLocator(action.locator)}).toContainText('${q(action.value)}');`;
593+
return ` await expect(${locatorExpr(action.locator)}).toContainText('${q(action.value)}');`;
543594
}
544595
default:
545596
return "";

0 commit comments

Comments
 (0)