@futdevpro/dynamo-e2e 1.18.13 → 1.18.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +24 -1
  3. package/build/_modules/generators/_collections/form-level-emission.util.d.ts.map +1 -1
  4. package/build/_modules/generators/_collections/form-level-emission.util.js +5 -1
  5. package/build/_modules/generators/_collections/form-level-emission.util.js.map +1 -1
  6. package/build/_modules/runtime/_collections/notification-detector-page-script.const.d.ts +28 -0
  7. package/build/_modules/runtime/_collections/notification-detector-page-script.const.d.ts.map +1 -0
  8. package/build/_modules/runtime/_collections/notification-detector-page-script.const.js +162 -0
  9. package/build/_modules/runtime/_collections/notification-detector-page-script.const.js.map +1 -0
  10. package/build/_modules/runtime/_collections/notification-detector.util.d.ts +47 -0
  11. package/build/_modules/runtime/_collections/notification-detector.util.d.ts.map +1 -0
  12. package/build/_modules/runtime/_collections/notification-detector.util.js +163 -0
  13. package/build/_modules/runtime/_collections/notification-detector.util.js.map +1 -0
  14. package/build/_modules/runtime/_models/notification-report.interface.d.ts +54 -0
  15. package/build/_modules/runtime/_models/notification-report.interface.d.ts.map +1 -0
  16. package/build/_modules/runtime/_models/notification-report.interface.js +3 -0
  17. package/build/_modules/runtime/_models/notification-report.interface.js.map +1 -0
  18. package/build/_modules/runtime/index.d.ts +3 -0
  19. package/build/_modules/runtime/index.d.ts.map +1 -1
  20. package/build/_modules/runtime/index.js +3 -0
  21. package/build/_modules/runtime/index.js.map +1 -1
  22. package/build/index.js +1 -1
  23. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,26 @@
3
3
  Minden release a `dynamo-e2e-foundation` HYPERPLAN MP-N ütemezést követi.
4
4
  Verzióformátum: `MM.mm.p` (két-számjegyes major-minor, Dynamo-konvenció).
5
5
 
6
+ ## [01.18.15] — 2026-09-24 — Live notification detector
7
+
8
+ - Added `DyE2E_NotificationDetector_Util` to `/runtime` (BFR-QA-001): `attach(context)` puts a `MutationObserver` into
9
+ every document BEFORE its scripts run and records each DyNX notification card / notifications area / mat-snackbar /
10
+ `[role=alert]` the moment it appears, classified error / warning / success / info — so an auto-dismissed toast is
11
+ not lost. `collect(page)` returns those plus the visible error banners (with a raw-technical-leak flag) and the
12
+ page's console errors/warnings and page errors; `summarize()` prints them.
13
+ - Captures go to Node through a Playwright binding, so they survive a full-page navigation (the redirect after
14
+ login). A capture is re-read 50 / 300 ms later to pick up content Angular renders after inserting the host element.
15
+ - The page-side code is plain JavaScript (`DYE2E_NOTIFICATION_DETECTOR_PAGE_SCRIPT`), so no `__name` shim is needed.
16
+ - 12 specs, 9 of them in a real Chromium (fixture pages served by `route`); the browser specs are reported as pending
17
+ with the reason when no Chromium can be started.
18
+
19
+ ## [01.18.14] — 2026-09-23 — The form-level empty-submit case honours `fieldErrorSelector`
20
+
21
+ - `DyE2E_FormLevelEmission_Util.emitSubmitEmpty` now uses the form's `fieldErrorSelector` (default unchanged:
22
+ `.text-red-400.mt-1`), exactly like the field-level cases. Before, it hard-coded the Tailwind class pair, so a suite
23
+ generated with a semantic selector (e.g. `[data-testid$="-error"]`) still asserted on STYLE once per form
24
+ (BFR-MASTERPROMPTER-070).
25
+
6
26
  ## [01.18.13] — 2026-09-23 — Generated specs stay text (control characters escaped)
7
27
 
8
28
  - `DyE2E_SpecEmission_Util.escapeString` now emits every remaining control character (NUL / BS / DEL …) and the
package/README.md CHANGED
@@ -68,9 +68,32 @@ const specSource = DyE2E_FormSuite_Generator.emit({
68
68
 
69
69
  - `@futdevpro/dynamo-e2e/contracts` — POJO types
70
70
  - `@futdevpro/dynamo-e2e/generators` — Node-only emission engine
71
- - `@futdevpro/dynamo-e2e/runtime` — Playwright fixtures + page-objects (Wave-4)
71
+ - `@futdevpro/dynamo-e2e/runtime` — Playwright fixtures + page-objects (Wave-4) + the live notification detector
72
+ (`DyE2E_NotificationDetector_Util`, see below)
72
73
  - `@futdevpro/dynamo-e2e/presets` — pre-built suites + `DyE2E_getPlaywrightConfig` / `DyE2E_getGlobalSetup` playwright-config factory (fleet e2e-config SSOT)
73
74
  - `@futdevpro/dynamo-e2e/game` — capability-gated deterministic game seam, failure artifacts and bidirectional journey-catalog validation
74
75
  - `@futdevpro/dynamo-e2e/cwv` — Core Web Vitals (Wave-3)
75
76
 
77
+ ## Live notification detector (`/runtime`)
78
+
79
+ Records every user-visible notification — DyNX `dynamo-notification-card` / `dynamo-notifications-area`, mat-snackbar
80
+ (`red-` / `yellow-` / `blue-` / `green-snackbar`), `[role=alert]` — at the moment it APPEARS, so a toast that
81
+ auto-dismissed before the end of the test is still in the report. It also snapshots the visible error banners (with a
82
+ `rawTechnicalLeak` flag for texts like `Http failure response`, `/api/`, `[object Object]`) and collects the page's
83
+ `console` errors/warnings and uncaught `pageerror`s.
84
+
85
+ ```ts
86
+ import { DyE2E_NotificationDetector_Util } from '@futdevpro/dynamo-e2e/runtime';
87
+
88
+ await DyE2E_NotificationDetector_Util.attach(context); // before the first navigation
89
+ // ... the flow ...
90
+ const report = await DyE2E_NotificationDetector_Util.collect(page);
91
+ expect(report.notifications.filter((n) => n.type === 'error')).toEqual([]);
92
+ console.log(DyE2E_NotificationDetector_Util.summarize(report, 'login'));
93
+ ```
94
+
95
+ Captures survive a full-page navigation (they are reported to Node through a Playwright binding). The page-side code
96
+ is plain JavaScript, so no `__name` shim is needed under `tsx` / esbuild. `collect()` throws on a context without
97
+ `attach()` — an empty report would otherwise read as "no notifications".
98
+
76
99
  Detailed game-preset contract: [`__documentations/game-preset.md`](__documentations/game-preset.md).
@@ -1 +1 @@
1
- {"version":3,"file":"form-level-emission.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/generators/_collections/form-level-emission.util.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAGpF;;;GAGG;AACH,qBAAa,4BAA4B;IAEvC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAyB9D,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAQ9D,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAM/D,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;CAOvD"}
1
+ {"version":3,"file":"form-level-emission.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/generators/_collections/form-level-emission.util.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAGpF;;;GAGG;AACH,qBAAa,4BAA4B;IAEvC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IA8B9D,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAQ9D,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAM/D,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;CAOvD"}
@@ -20,12 +20,16 @@ class DyE2E_FormLevelEmission_Util {
20
20
  await expect(submit).toBeDisabled();
21
21
  });`;
22
22
  }
23
+ // BFR-MASTERPROMPTER-070 — the SAME configurable field-error selector as the field-level cases
24
+ // (`DyE2E_FieldEmission_Util`); before this the form-level case hard-coded the Tailwind class pair, so a suite with
25
+ // a semantic `fieldErrorSelector` still asserted on STYLE once per form.
26
+ const errorSelector = form.fieldErrorSelector ?? '.text-red-400.mt-1';
23
27
  return `test('form-level: submit empty form ${requiredFieldCount > 0 ? '→ required-validators fire' : '→ submits'}', async ({ page }): Promise<void> => {
24
28
  const submit: Locator = page.getByTestId('${submitTestId}');
25
29
  await submit.click();
26
30
  ${requiredFieldCount > 0
27
31
  ? ` // ${requiredFieldCount} required field(s) — error-msgek vizualisak
28
- await expect(page.locator('.text-red-400.mt-1').first()).toBeVisible();`
32
+ await expect(page.locator('${errorSelector}').first()).toBeVisible();`
29
33
  : ` // no required fields — submit goes through`}
30
34
  });`;
31
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"form-level-emission.util.js","sourceRoot":"","sources":["../../../../src/_modules/generators/_collections/form-level-emission.util.ts"],"names":[],"mappings":";;;AAAA,kFAAoF;AAIpF;;;GAGG;AACH,MAAa,4BAA4B;IAEvC,MAAM,CAAC,eAAe,CAAC,IAA8B;QACnD,mFAAmF;QACnF,kEAAkE;QAClE,MAAM,YAAY,GAAW,IAAI,CAAC,YAAY,IAAI,gCAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzF,MAAM,kBAAkB,GAAW,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;QAElG,iFAAiF;QACjF,gEAAgE;QAChE,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACnC,OAAO;8CACiC,YAAY;;IAEtD,CAAC;QACD,CAAC;QAED,OAAO,uCAAuC,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,WAAW;8CACvE,YAAY;;EAExD,kBAAkB,GAAG,CAAC;YACtB,CAAC,CAAC,QAAQ,kBAAkB;0EAC4C;YACxE,CAAC,CAAC,+CAA+C;IAC/C,CAAC;IACH,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,IAA8B;QACnD,OAAO;;;;IAIP,CAAC;IACH,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,IAA8B;QACpD,OAAO;;IAEP,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IAA8B;QAC3C,OAAO;YACL,4BAA4B,CAAC,eAAe,CAAC,IAAI,CAAC;YAClD,4BAA4B,CAAC,eAAe,CAAC,IAAI,CAAC;YAClD,4BAA4B,CAAC,gBAAgB,CAAC,IAAI,CAAC;SACpD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC;CACF;AAhDD,oEAgDC"}
1
+ {"version":3,"file":"form-level-emission.util.js","sourceRoot":"","sources":["../../../../src/_modules/generators/_collections/form-level-emission.util.ts"],"names":[],"mappings":";;;AAAA,kFAAoF;AAIpF;;;GAGG;AACH,MAAa,4BAA4B;IAEvC,MAAM,CAAC,eAAe,CAAC,IAA8B;QACnD,mFAAmF;QACnF,kEAAkE;QAClE,MAAM,YAAY,GAAW,IAAI,CAAC,YAAY,IAAI,gCAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzF,MAAM,kBAAkB,GAAW,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;QAElG,iFAAiF;QACjF,gEAAgE;QAChE,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACnC,OAAO;8CACiC,YAAY;;IAEtD,CAAC;QACD,CAAC;QAED,+FAA+F;QAC/F,oHAAoH;QACpH,yEAAyE;QACzE,MAAM,aAAa,GAAW,IAAI,CAAC,kBAAkB,IAAI,oBAAoB,CAAC;QAE9E,OAAO,uCAAuC,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,WAAW;8CACvE,YAAY;;EAExD,kBAAkB,GAAG,CAAC;YACtB,CAAC,CAAC,QAAQ,kBAAkB;+BACC,aAAa,4BAA4B;YACtE,CAAC,CAAC,+CAA+C;IAC/C,CAAC;IACH,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,IAA8B;QACnD,OAAO;;;;IAIP,CAAC;IACH,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,IAA8B;QACpD,OAAO;;IAEP,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IAA8B;QAC3C,OAAO;YACL,4BAA4B,CAAC,eAAe,CAAC,IAAI,CAAC;YAClD,4BAA4B,CAAC,eAAe,CAAC,IAAI,CAAC;YAClD,4BAA4B,CAAC,gBAAgB,CAAC,IAAI,CAAC;SACpD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC;CACF;AArDD,oEAqDC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Name of the Playwright binding the page script reports every capture to (BFR-QA-001). Node keeps the
3
+ * captures, so a notification survives a full-page navigation (e.g. the redirect after login) and the page close.
4
+ */
5
+ export declare const DYE2E_NOTIFICATION_BINDING_NAME: string;
6
+ /**
7
+ * Page-side notification detector — `addInitScript` input (BFR-QA-001).
8
+ *
9
+ * **PURE JavaScript** (not TypeScript), like `DYE2E_WEB_VITALS_PAGE_SCRIPT`: the browser runs it as-is, so no
10
+ * transpiler helper (`__name` of tsx/esbuild) can leak into the page — no shim is needed in any consumer setup.
11
+ *
12
+ * Observes the document from its very start (a `MutationObserver` on `document`, subtree) and records every DyNX
13
+ * notification card / notifications area / mat-snackbar / `[role=alert]` at the moment it is ADDED — so an
14
+ * auto-dismissed toast (DyNX error 14 s, others 7 s) is not lost. A capture is re-read 50 ms and 300 ms later to
15
+ * fill in what Angular renders after inserting the host element (title, message, colour class); the re-read keeps
16
+ * the capture id. Every capture (and re-read) goes to the Node binding, and to `window.__dye2e_notifications__`.
17
+ *
18
+ * Type classification (from `@futdevpro/ngx-dynamo` notifications): `bg-red-50` / `red-snackbar` / mat-icon `error`
19
+ * → error · `bg-orange-50` / `yellow-snackbar` / `warning` → warning · `bg-green-50` / `green-snackbar` /
20
+ * `check_circle` → success · `bg-blue-50` / `blue-snackbar` → info.
21
+ */
22
+ export declare const DYE2E_NOTIFICATION_DETECTOR_PAGE_SCRIPT: string;
23
+ /**
24
+ * Page-side expression: the visible error banners / inline error texts at this moment (not toasts — e.g. a login
25
+ * form showing a raw HTTP error). Only really visible elements, deduplicated by tag + text. PURE JavaScript.
26
+ */
27
+ export declare const DYE2E_VISIBLE_ERRORS_PAGE_EXPRESSION: string;
28
+ //# sourceMappingURL=notification-detector-page-script.const.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-detector-page-script.const.d.ts","sourceRoot":"","sources":["../../../../src/_modules/runtime/_collections/notification-detector-page-script.const.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAyC,CAAC;AAExF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uCAAuC,EAAE,MA6GrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oCAAoC,EAAE,MAsBlD,CAAC"}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DYE2E_VISIBLE_ERRORS_PAGE_EXPRESSION = exports.DYE2E_NOTIFICATION_DETECTOR_PAGE_SCRIPT = exports.DYE2E_NOTIFICATION_BINDING_NAME = void 0;
4
+ /**
5
+ * Name of the Playwright binding the page script reports every capture to (BFR-QA-001). Node keeps the
6
+ * captures, so a notification survives a full-page navigation (e.g. the redirect after login) and the page close.
7
+ */
8
+ exports.DYE2E_NOTIFICATION_BINDING_NAME = '__dye2e_notification_binding__';
9
+ /**
10
+ * Page-side notification detector — `addInitScript` input (BFR-QA-001).
11
+ *
12
+ * **PURE JavaScript** (not TypeScript), like `DYE2E_WEB_VITALS_PAGE_SCRIPT`: the browser runs it as-is, so no
13
+ * transpiler helper (`__name` of tsx/esbuild) can leak into the page — no shim is needed in any consumer setup.
14
+ *
15
+ * Observes the document from its very start (a `MutationObserver` on `document`, subtree) and records every DyNX
16
+ * notification card / notifications area / mat-snackbar / `[role=alert]` at the moment it is ADDED — so an
17
+ * auto-dismissed toast (DyNX error 14 s, others 7 s) is not lost. A capture is re-read 50 ms and 300 ms later to
18
+ * fill in what Angular renders after inserting the host element (title, message, colour class); the re-read keeps
19
+ * the capture id. Every capture (and re-read) goes to the Node binding, and to `window.__dye2e_notifications__`.
20
+ *
21
+ * Type classification (from `@futdevpro/ngx-dynamo` notifications): `bg-red-50` / `red-snackbar` / mat-icon `error`
22
+ * → error · `bg-orange-50` / `yellow-snackbar` / `warning` → warning · `bg-green-50` / `green-snackbar` /
23
+ * `check_circle` → success · `bg-blue-50` / `blue-snackbar` → info.
24
+ */
25
+ exports.DYE2E_NOTIFICATION_DETECTOR_PAGE_SCRIPT = `
26
+ (function () {
27
+ var w = window;
28
+ if (w.__dye2e_notifications__) { return; }
29
+ var list = [];
30
+ w.__dye2e_notifications__ = list;
31
+ var bindingName = '${exports.DYE2E_NOTIFICATION_BINDING_NAME}';
32
+ var perf = w.performance;
33
+ var origin = String(Math.round(perf && perf.timeOrigin ? perf.timeOrigin : Date.now()));
34
+ var counter = 0;
35
+ var recentKeys = {};
36
+ var recorded = {};
37
+ var checks = [
38
+ ['dynx-card', 'dynamo-notification-card'],
39
+ ['notif-area', 'dynamo-notifications-area'],
40
+ ['snackbar', 'mat-snack-bar-container, .mat-mdc-snack-bar-container, simple-snack-bar, ' +
41
+ '.red-snackbar, .yellow-snackbar, .green-snackbar, .blue-snackbar'],
42
+ ['role-alert', '[role="alert"], [role="alertdialog"]']
43
+ ];
44
+
45
+ function nowMs() { return perf ? perf.now() : 0; }
46
+ function squash(text) { return (text || '').replace(/\\s+/g, ' ').trim(); }
47
+ function textOf(el, selector) {
48
+ try {
49
+ var node = el.querySelector(selector);
50
+ return node ? squash(node.textContent) : '';
51
+ } catch (e) { return ''; }
52
+ }
53
+ function classify(el) {
54
+ var html = el.outerHTML || '';
55
+ if (/bg-red-50|red-snackbar|>\\s*error\\s*</.test(html)) { return 'error'; }
56
+ if (/bg-orange-50|yellow-snackbar|>\\s*warning\\s*</.test(html)) { return 'warning'; }
57
+ if (/bg-green-50|green-snackbar|>\\s*check_circle\\s*</.test(html)) { return 'success'; }
58
+ if (/bg-blue-50|blue-snackbar/.test(html)) { return 'info'; }
59
+ return 'unknown';
60
+ }
61
+ function read(el) {
62
+ var structured = textOf(el, '.text-slate-700') || textOf(el, '.mat-mdc-snack-bar-label') ||
63
+ textOf(el, '[matsnackbarlabel]');
64
+ return {
65
+ type: classify(el),
66
+ structured: !!structured,
67
+ title: (structured || squash(el.textContent)).slice(0, 400),
68
+ message: textOf(el, '.text-slate-500').slice(0, 400),
69
+ html: squash(el.outerHTML).slice(0, 500)
70
+ };
71
+ }
72
+ function emit(entry) {
73
+ try {
74
+ if (typeof w[bindingName] === 'function') { w[bindingName](entry); }
75
+ } catch (e) { /* the page must never break because of the detector */ }
76
+ }
77
+ function enrich(entry, state, el) {
78
+ if (!el.isConnected) { return; }
79
+ var fresh = read(el);
80
+ var changed = false;
81
+ if (entry.type === 'unknown' && fresh.type !== 'unknown') { entry.type = fresh.type; changed = true; }
82
+ if (fresh.title && fresh.title !== entry.title && (!entry.title || !state.structured)) {
83
+ entry.title = fresh.title; state.structured = fresh.structured; changed = true;
84
+ }
85
+ if (!entry.message && fresh.message) { entry.message = fresh.message; changed = true; }
86
+ if (changed) { entry.html = fresh.html; emit(entry); }
87
+ }
88
+ function record(kind, el) {
89
+ var seen = recorded[kind] || (recorded[kind] = new WeakSet());
90
+ if (seen.has(el)) { return; }
91
+ seen.add(el);
92
+ var data = read(el);
93
+ var t = nowMs();
94
+ var key = kind + '|' + data.type + '|' + data.title;
95
+ if (data.title && recentKeys[key] !== undefined && t - recentKeys[key] < 1500) { return; }
96
+ recentKeys[key] = t;
97
+ counter += 1;
98
+ var entry = {
99
+ id: origin + '-' + counter,
100
+ kind: kind,
101
+ type: data.type,
102
+ title: data.title,
103
+ message: data.message,
104
+ url: location.href,
105
+ at: Date.now(),
106
+ sinceDocumentStartMs: Math.round(t),
107
+ html: data.html
108
+ };
109
+ var state = { structured: data.structured };
110
+ list.push(entry);
111
+ emit(entry);
112
+ setTimeout(function () { enrich(entry, state, el); }, 50);
113
+ setTimeout(function () { enrich(entry, state, el); }, 300);
114
+ }
115
+ function scan(node) {
116
+ if (!node || node.nodeType !== 1) { return; }
117
+ for (var i = 0; i < checks.length; i++) {
118
+ try {
119
+ if (node.matches(checks[i][1])) { record(checks[i][0], node); }
120
+ var found = node.querySelectorAll(checks[i][1]);
121
+ for (var j = 0; j < found.length; j++) { record(checks[i][0], found[j]); }
122
+ } catch (e) { /* one bad selector must not stop the other checks */ }
123
+ }
124
+ }
125
+
126
+ new MutationObserver(function (mutations) {
127
+ for (var i = 0; i < mutations.length; i++) {
128
+ var added = mutations[i].addedNodes;
129
+ for (var j = 0; j < added.length; j++) { scan(added[j]); }
130
+ }
131
+ }).observe(document, { childList: true, subtree: true });
132
+ scan(document.documentElement);
133
+ })();
134
+ `;
135
+ /**
136
+ * Page-side expression: the visible error banners / inline error texts at this moment (not toasts — e.g. a login
137
+ * form showing a raw HTTP error). Only really visible elements, deduplicated by tag + text. PURE JavaScript.
138
+ */
139
+ exports.DYE2E_VISIBLE_ERRORS_PAGE_EXPRESSION = `
140
+ (function () {
141
+ var selectors = ['.error-text', '[class*="error-text"]', 'mat-error', '.mat-mdc-form-field-error',
142
+ '.text-red-200', '.text-red-500', '.text-red-600', '.text-red-700', '[role="alert"]'];
143
+ var out = [];
144
+ var seen = {};
145
+ var found = document.querySelectorAll(selectors.join(','));
146
+ for (var i = 0; i < found.length; i++) {
147
+ var el = found[i];
148
+ var text = (el.textContent || '').replace(/\\s+/g, ' ').trim();
149
+ if (!text || text.length > 300 || !(el instanceof HTMLElement)) { continue; }
150
+ var rect = el.getBoundingClientRect();
151
+ var style = getComputedStyle(el);
152
+ var visible = el.offsetParent !== null && rect.width > 0 && rect.height > 0 &&
153
+ style.visibility !== 'hidden' && style.display !== 'none';
154
+ var key = el.tagName + '|' + text;
155
+ if (!visible || seen[key]) { continue; }
156
+ seen[key] = true;
157
+ out.push({ tag: el.tagName.toLowerCase(), cls: String(el.className || '').slice(0, 120), text: text.slice(0, 260) });
158
+ }
159
+ return out;
160
+ })()
161
+ `;
162
+ //# sourceMappingURL=notification-detector-page-script.const.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-detector-page-script.const.js","sourceRoot":"","sources":["../../../../src/_modules/runtime/_collections/notification-detector-page-script.const.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACU,QAAA,+BAA+B,GAAW,gCAAgC,CAAC;AAExF;;;;;;;;;;;;;;;GAeG;AACU,QAAA,uCAAuC,GAAW;;;;;;uBAMxC,uCAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuGrD,CAAC;AAEF;;;GAGG;AACU,QAAA,oCAAoC,GAAW;;;;;;;;;;;;;;;;;;;;;;CAsB3D,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { BrowserContext, Page } from '@playwright/test';
2
+ import { DyE2E_NotificationReport_Interface, DyE2E_VisibleError_Interface } from '../_models/notification-report.interface';
3
+ /**
4
+ * Live notification detector (BFR-QA-001) — records every user-visible notification / toast / snackbar /
5
+ * `[role=alert]` at the moment it APPEARS, so an auto-dismissed toast is still in the report; plus the visible
6
+ * error banners (with a raw-technical-leak flag) and the page's `console` error/warning + `pageerror`.
7
+ *
8
+ * Why: console mining does not see the framework's user notifications (they are DOM, without a console log), and
9
+ * a final screenshot loses the ones that disappeared. A QA run missed a login error toast exactly this way.
10
+ *
11
+ * ```ts
12
+ * await DyE2E_NotificationDetector_Util.attach(context); // BEFORE the first navigation
13
+ * const page: Page = await context.newPage();
14
+ * await page.goto('/login'); // ... the flow ...
15
+ * const report: DyE2E_NotificationReport_Interface = await DyE2E_NotificationDetector_Util.collect(page);
16
+ * expect(report.notifications.filter((n) => n.type === 'error')).toEqual([]);
17
+ * console.log(DyE2E_NotificationDetector_Util.summarize(report, 'login'));
18
+ * ```
19
+ */
20
+ export declare class DyE2E_NotificationDetector_Util {
21
+ /** Texts that expose internals to the user instead of a human message. */
22
+ static readonly rawTechnicalLeakPattern: RegExp;
23
+ private static readonly attachedContexts;
24
+ private static readonly pageStates;
25
+ /**
26
+ * Attaches the detector to a context: every document of every page (and frame) gets the observer BEFORE its
27
+ * own scripts run; pages that already exist are instrumented immediately (their current DOM is scanned).
28
+ * Calling it again for the same context is a no-op.
29
+ */
30
+ static attach(context: BrowserContext): Promise<void>;
31
+ /**
32
+ * The report of one page: every notification captured so far (across its navigations, in order of appearance),
33
+ * the error banners visible NOW, and the console errors/warnings + page errors.
34
+ * @throws if the page's context has no detector attached — an empty report would be a false "all clear".
35
+ */
36
+ static collect(page: Page): Promise<DyE2E_NotificationReport_Interface>;
37
+ /** The error banners / inline error texts visible on the page right now, with the raw-technical-leak flag. */
38
+ static scanVisibleErrors(page: Page): Promise<DyE2E_VisibleError_Interface[]>;
39
+ /** `true` when the text shows the user raw technical detail (HTTP error text, API path, `[object Object]` …). */
40
+ static isRawTechnicalLeak(text: string): boolean;
41
+ /** Short human-readable summary (CLI output / QA round log). */
42
+ static summarize(report: DyE2E_NotificationReport_Interface, label?: string): string;
43
+ /** Console + pageerror listeners, once per page. */
44
+ private static watchPage;
45
+ private static stateOf;
46
+ }
47
+ //# sourceMappingURL=notification-detector.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-detector.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/runtime/_collections/notification-detector.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAkB,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,EAGL,kCAAkC,EAGlC,4BAA4B,EAC7B,MAAM,0CAA0C,CAAC;AAqBlD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,+BAA+B;IAE1C,0EAA0E;IAC1E,MAAM,CAAC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAI7C;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA0D;IAClG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAC2B;IAE7D;;;;OAIG;WACU,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B3D;;;;OAIG;WACU,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,kCAAkC,CAAC;IAiC7E,8GAA8G;WACjG,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,4BAA4B,EAAE,CAAC;IAenF,iHAAiH;IACjH,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIhD,gEAAgE;IAChE,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,kCAAkC,EAAE,KAAK,GAAE,MAAW,GAAG,MAAM;IA+BxF,oDAAoD;IACpD,OAAO,CAAC,MAAM,CAAC,SAAS;IAgBxB,OAAO,CAAC,MAAM,CAAC,OAAO;CAQvB"}
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DyE2E_NotificationDetector_Util = void 0;
4
+ const notification_detector_page_script_const_1 = require("./notification-detector-page-script.const");
5
+ /**
6
+ * Live notification detector (BFR-QA-001) — records every user-visible notification / toast / snackbar /
7
+ * `[role=alert]` at the moment it APPEARS, so an auto-dismissed toast is still in the report; plus the visible
8
+ * error banners (with a raw-technical-leak flag) and the page's `console` error/warning + `pageerror`.
9
+ *
10
+ * Why: console mining does not see the framework's user notifications (they are DOM, without a console log), and
11
+ * a final screenshot loses the ones that disappeared. A QA run missed a login error toast exactly this way.
12
+ *
13
+ * ```ts
14
+ * await DyE2E_NotificationDetector_Util.attach(context); // BEFORE the first navigation
15
+ * const page: Page = await context.newPage();
16
+ * await page.goto('/login'); // ... the flow ...
17
+ * const report: DyE2E_NotificationReport_Interface = await DyE2E_NotificationDetector_Util.collect(page);
18
+ * expect(report.notifications.filter((n) => n.type === 'error')).toEqual([]);
19
+ * console.log(DyE2E_NotificationDetector_Util.summarize(report, 'login'));
20
+ * ```
21
+ */
22
+ class DyE2E_NotificationDetector_Util {
23
+ /** Texts that expose internals to the user instead of a human message. */
24
+ static rawTechnicalLeakPattern = new RegExp('(Http failure response|Unauthorized|Internal Server|undefined|\\[object Object\\]|/api/|-api/|' +
25
+ 'status of \\d{3}|ERR_|Cannot (GET|POST|read))', 'i');
26
+ static attachedContexts = new WeakSet();
27
+ static pageStates = new WeakMap();
28
+ /**
29
+ * Attaches the detector to a context: every document of every page (and frame) gets the observer BEFORE its
30
+ * own scripts run; pages that already exist are instrumented immediately (their current DOM is scanned).
31
+ * Calling it again for the same context is a no-op.
32
+ */
33
+ static async attach(context) {
34
+ if (this.attachedContexts.has(context)) {
35
+ return;
36
+ }
37
+ this.attachedContexts.add(context);
38
+ // the page reports every capture here — the Node-side store survives navigations
39
+ await context.exposeBinding(notification_detector_page_script_const_1.DYE2E_NOTIFICATION_BINDING_NAME, (source, entry) => {
40
+ if (source.page && entry && typeof entry.id === 'string') {
41
+ this.stateOf(source.page).notifications.set(entry.id, entry);
42
+ }
43
+ });
44
+ await context.addInitScript({ content: notification_detector_page_script_const_1.DYE2E_NOTIFICATION_DETECTOR_PAGE_SCRIPT });
45
+ context.on('page', (page) => this.watchPage(page));
46
+ for (const page of context.pages()) {
47
+ this.watchPage(page);
48
+ try {
49
+ await page.evaluate(notification_detector_page_script_const_1.DYE2E_NOTIFICATION_DETECTOR_PAGE_SCRIPT);
50
+ }
51
+ catch (error) {
52
+ // the page is navigating / closed — the init script reaches its next document anyway
53
+ }
54
+ }
55
+ }
56
+ /**
57
+ * The report of one page: every notification captured so far (across its navigations, in order of appearance),
58
+ * the error banners visible NOW, and the console errors/warnings + page errors.
59
+ * @throws if the page's context has no detector attached — an empty report would be a false "all clear".
60
+ */
61
+ static async collect(page) {
62
+ if (!this.attachedContexts.has(page.context())) {
63
+ throw new Error('DyE2E_NotificationDetector_Util.collect: no detector is attached to this page\'s context — call ' +
64
+ '`await DyE2E_NotificationDetector_Util.attach(context)` before the first navigation. Without it the ' +
65
+ 'report would be empty and read as "no notifications".');
66
+ }
67
+ const state = this.stateOf(page);
68
+ // merge the page's own list too (by id): binding calls may still be in flight
69
+ let inPage = [];
70
+ try {
71
+ inPage = await page.evaluate('window.__dye2e_notifications__ || []');
72
+ }
73
+ catch (error) {
74
+ // the page closed / is navigating — the Node-side store is still complete
75
+ }
76
+ const merged = new Map(state.notifications);
77
+ for (const entry of inPage) {
78
+ merged.set(entry.id, entry);
79
+ }
80
+ return {
81
+ notifications: [...merged.values()].sort((a, b) => a.at - b.at),
82
+ visibleErrors: await this.scanVisibleErrors(page),
83
+ consoleErrors: [...state.consoleErrors],
84
+ pageErrors: [...state.pageErrors],
85
+ };
86
+ }
87
+ /** The error banners / inline error texts visible on the page right now, with the raw-technical-leak flag. */
88
+ static async scanVisibleErrors(page) {
89
+ let raw = [];
90
+ try {
91
+ raw = await page.evaluate(notification_detector_page_script_const_1.DYE2E_VISIBLE_ERRORS_PAGE_EXPRESSION);
92
+ }
93
+ catch (error) {
94
+ return [];
95
+ }
96
+ return raw.map((item) => ({
97
+ tag: item.tag,
98
+ cls: item.cls,
99
+ text: item.text,
100
+ rawTechnicalLeak: this.isRawTechnicalLeak(item.text),
101
+ }));
102
+ }
103
+ /** `true` when the text shows the user raw technical detail (HTTP error text, API path, `[object Object]` …). */
104
+ static isRawTechnicalLeak(text) {
105
+ return this.rawTechnicalLeakPattern.test(text || '');
106
+ }
107
+ /** Short human-readable summary (CLI output / QA round log). */
108
+ static summarize(report, label = '') {
109
+ const byType = { error: 0, warning: 0, success: 0, info: 0, unknown: 0 };
110
+ report.notifications.forEach((n) => {
111
+ byType[n.type] = byType[n.type] + 1;
112
+ });
113
+ const lines = [];
114
+ lines.push('=== NOTIFICATION DETECTOR' + (label ? ' — ' + label : '') + ' ===');
115
+ lines.push('Notifications: ' + report.notifications.length + ' (error=' + byType.error + ' warning=' +
116
+ byType.warning + ' success=' + byType.success + ' info=' + byType.info + ' unknown=' + byType.unknown + ')');
117
+ report.notifications.forEach((n, i) => {
118
+ lines.push(' [' + (i + 1) + '] ' + n.type.toUpperCase() + ' <' + n.kind + '> "' + n.title.slice(0, 160) + '"' +
119
+ (n.message ? ' — ' + n.message.slice(0, 120) : ''));
120
+ });
121
+ const leaks = report.visibleErrors.filter((e) => e.rawTechnicalLeak).length;
122
+ lines.push('Visible error banners: ' + report.visibleErrors.length + ' (raw-technical-leak: ' + leaks + ')');
123
+ report.visibleErrors.forEach((e, i) => {
124
+ lines.push(' [E' + (i + 1) + '] ' + (e.rawTechnicalLeak ? 'LEAK ' : '') + '<' + e.tag + '.' +
125
+ (e.cls.split(' ')[0] || '') + '> "' + e.text.slice(0, 200) + '"');
126
+ });
127
+ lines.push('Console errors/warnings: ' + report.consoleErrors.length + ' | pageerrors: ' +
128
+ report.pageErrors.length);
129
+ report.consoleErrors.slice(0, 12).forEach((c) => {
130
+ lines.push(' console.' + c.type + ': ' + c.text.slice(0, 180));
131
+ });
132
+ report.pageErrors.slice(0, 8).forEach((p) => {
133
+ lines.push(' pageerror: ' + p.text.slice(0, 180));
134
+ });
135
+ return lines.join('\n');
136
+ }
137
+ /** Console + pageerror listeners, once per page. */
138
+ static watchPage(page) {
139
+ if (this.pageStates.has(page)) {
140
+ return;
141
+ }
142
+ const state = this.stateOf(page);
143
+ page.on('console', (message) => {
144
+ const type = message.type();
145
+ if (type === 'error' || type === 'warning') {
146
+ state.consoleErrors.push({ type: type, text: (message.text() || '').slice(0, 500), url: page.url() });
147
+ }
148
+ });
149
+ page.on('pageerror', (error) => {
150
+ state.pageErrors.push({ text: String(error?.message || error).slice(0, 500), url: page.url() });
151
+ });
152
+ }
153
+ static stateOf(page) {
154
+ let state = this.pageStates.get(page);
155
+ if (!state) {
156
+ state = { notifications: new Map(), consoleErrors: [], pageErrors: [] };
157
+ this.pageStates.set(page, state);
158
+ }
159
+ return state;
160
+ }
161
+ }
162
+ exports.DyE2E_NotificationDetector_Util = DyE2E_NotificationDetector_Util;
163
+ //# sourceMappingURL=notification-detector.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-detector.util.js","sourceRoot":"","sources":["../../../../src/_modules/runtime/_collections/notification-detector.util.ts"],"names":[],"mappings":";;;AAUA,uGAImD;AAgBnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,+BAA+B;IAE1C,0EAA0E;IAC1E,MAAM,CAAU,uBAAuB,GAAW,IAAI,MAAM,CAC1D,gGAAgG;QAChG,+CAA+C,EAC/C,GAAG,CACJ,CAAC;IAEM,MAAM,CAAU,gBAAgB,GAA4B,IAAI,OAAO,EAAkB,CAAC;IAC1F,MAAM,CAAU,UAAU,GAChC,IAAI,OAAO,EAA+C,CAAC;IAE7D;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAuB;QACzC,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEnC,iFAAiF;QACjF,MAAM,OAAO,CAAC,aAAa,CACzB,yEAA+B,EAC/B,CAAC,MAAsB,EAAE,KAAmC,EAAQ,EAAE;YACpE,IAAI,MAAM,CAAC,IAAI,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBACzD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC,CACF,CAAC;QACF,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,iFAAuC,EAAE,CAAC,CAAC;QAElF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAU,EAAQ,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACnC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,QAAQ,CAAC,iFAAuC,CAAC,CAAC;YAC/D,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,qFAAqF;YACvF,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAU;QAC7B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CACb,kGAAkG;gBAClG,sGAAsG;gBACtG,uDAAuD,CACxD,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAA0C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAExE,8EAA8E;QAC9E,IAAI,MAAM,GAAmC,EAAE,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAiC,sCAAsC,CAAC,CAAC;QACvG,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,0EAA0E;QAC5E,CAAC;QACD,MAAM,MAAM,GACV,IAAI,GAAG,CAAuC,KAAK,CAAC,aAAa,CAAC,CAAC;QACrE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC;QAED,OAAO;YACL,aAAa,EAAE,CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAE,CAAC,IAAI,CACxC,CAAC,CAA+B,EAAE,CAA+B,EAAU,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAC1F;YACD,aAAa,EAAE,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;YACjD,aAAa,EAAE,CAAE,GAAG,KAAK,CAAC,aAAa,CAAE;YACzC,UAAU,EAAE,CAAE,GAAG,KAAK,CAAC,UAAU,CAAE;SACpC,CAAC;IACJ,CAAC;IAED,8GAA8G;IAC9G,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAU;QACvC,IAAI,GAAG,GAAsC,EAAE,CAAC;QAChD,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAoC,8EAAoC,CAAC,CAAC;QACrG,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAqC,EAAgC,EAAE,CAAC,CAAC;YACvF,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;SACrD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,iHAAiH;IACjH,MAAM,CAAC,kBAAkB,CAAC,IAAY;QACpC,OAAO,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,gEAAgE;IAChE,MAAM,CAAC,SAAS,CAAC,MAA0C,EAAE,QAAgB,EAAE;QAC7E,MAAM,MAAM,GAA2C,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACjH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAA+B,EAAQ,EAAE;YACrE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,2BAA2B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC,KAAK,GAAG,WAAW;YACnG,MAAM,CAAC,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,IAAI,GAAG,WAAW,GAAG,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;QAC/G,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAA+B,EAAE,CAAS,EAAQ,EAAE;YAChF,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG;gBAC5G,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAW,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAA+B,EAAW,EAAE,CAC7F,CAAC,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,yBAAyB,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,yBAAyB,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;QAC9G,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAA+B,EAAE,CAAS,EAAQ,EAAE;YAChF,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,GAAG;gBAC1F,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,2BAA2B,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,kBAAkB;YACvF,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5B,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAA+B,EAAQ,EAAE;YAClF,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAA4B,EAAQ,EAAE;YAC3E,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,oDAAoD;IAC5C,MAAM,CAAC,SAAS,CAAC,IAAU;QACjC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAA0C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxE,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAuB,EAAQ,EAAE;YACnD,MAAM,IAAI,GAAW,OAAO,CAAC,IAAI,EAAE,CAAC;YACpC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC3C,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACxG,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAY,EAAQ,EAAE;YAC1C,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClG,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,OAAO,CAAC,IAAU;QAC/B,IAAI,KAAK,GAAsD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,aAAa,EAAE,IAAI,GAAG,EAAwC,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;YAC9G,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;;AAjKH,0EAkKC"}
@@ -0,0 +1,54 @@
1
+ /** Severity of a captured notification, read from the DyNX colour classes / snackbar panel class / mat-icon. */
2
+ export type DyE2E_NotificationType = 'error' | 'warning' | 'success' | 'info' | 'unknown';
3
+ /** Which UI surface produced the notification. */
4
+ export type DyE2E_NotificationKind = 'dynx-card' | 'notif-area' | 'snackbar' | 'role-alert';
5
+ /**
6
+ * One user-visible notification, recorded at the moment it APPEARED in the DOM (BFR-QA-001) —
7
+ * so it is in the report even if it auto-dismissed before collection.
8
+ */
9
+ export interface DyE2E_Notification_Interface {
10
+ /** Unique per capture (`<document time origin>-<counter>`); an enriched re-read keeps the same id. */
11
+ id: string;
12
+ kind: DyE2E_NotificationKind;
13
+ type: DyE2E_NotificationType;
14
+ /** Title (DyNX `.text-slate-700`, snackbar label) — falls back to the whole text. */
15
+ title: string;
16
+ /** DyNX body text (`.text-slate-500`), empty when there is none. */
17
+ message: string;
18
+ /** `location.href` of the document the notification appeared in. */
19
+ url: string;
20
+ /** Wall-clock time of the appearance (epoch ms). */
21
+ at: number;
22
+ /** Milliseconds since that document started loading (`performance.now()`). */
23
+ sinceDocumentStartMs: number;
24
+ /** Whitespace-collapsed `outerHTML`, capped at 500 chars. */
25
+ html: string;
26
+ }
27
+ /** A visible, persistent (non-toast) error banner or inline error text at collection time. */
28
+ export interface DyE2E_VisibleError_Interface {
29
+ tag: string;
30
+ cls: string;
31
+ text: string;
32
+ /** The text leaks raw technical detail to the user (`Http failure response`, `/api/`, `[object Object]` …). */
33
+ rawTechnicalLeak: boolean;
34
+ }
35
+ /** A `console.error` / `console.warn` message of the page. */
36
+ export interface DyE2E_ConsoleEntry_Interface {
37
+ type: string;
38
+ text: string;
39
+ url: string;
40
+ }
41
+ /** An uncaught page exception (`pageerror`). */
42
+ export interface DyE2E_PageError_Interface {
43
+ text: string;
44
+ url: string;
45
+ }
46
+ /** Everything the notification detector knows about one page. */
47
+ export interface DyE2E_NotificationReport_Interface {
48
+ /** In order of appearance, across every document the page navigated through. */
49
+ notifications: DyE2E_Notification_Interface[];
50
+ visibleErrors: DyE2E_VisibleError_Interface[];
51
+ consoleErrors: DyE2E_ConsoleEntry_Interface[];
52
+ pageErrors: DyE2E_PageError_Interface[];
53
+ }
54
+ //# sourceMappingURL=notification-report.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-report.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/runtime/_models/notification-report.interface.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1F,kDAAkD;AAClD,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,YAAY,GAAG,UAAU,GAAG,YAAY,CAAC;AAE5F;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,sGAAsG;IACtG,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,EAAE,sBAAsB,CAAC;IAC7B,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,8EAA8E;IAC9E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8FAA8F;AAC9F,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,+GAA+G;IAC/G,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,8DAA8D;AAC9D,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,gDAAgD;AAChD,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,iEAAiE;AACjE,MAAM,WAAW,kCAAkC;IACjD,gFAAgF;IAChF,aAAa,EAAE,4BAA4B,EAAE,CAAC;IAC9C,aAAa,EAAE,4BAA4B,EAAE,CAAC;IAC9C,aAAa,EAAE,4BAA4B,EAAE,CAAC;IAC9C,UAAU,EAAE,yBAAyB,EAAE,CAAC;CACzC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=notification-report.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-report.interface.js","sourceRoot":"","sources":["../../../../src/_modules/runtime/_models/notification-report.interface.ts"],"names":[],"mappings":""}
@@ -13,9 +13,12 @@
13
13
  * ```
14
14
  */
15
15
  export * from './_collections/assertion-helpers.util';
16
+ export * from './_collections/notification-detector-page-script.const';
17
+ export * from './_collections/notification-detector.util';
16
18
  export * from './_collections/unique-generator.util';
17
19
  export * from './_collections/wait-helpers.util';
18
20
  export * from './_models/index';
21
+ export * from './_models/notification-report.interface';
19
22
  export * from './_models/test-user.interface';
20
23
  export * from './_services/dialog-page-object';
21
24
  export * from './_services/field-page-object';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iBAAiB,CAAC;AAChC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,YAAY,CAAC;AAE3B,eAAO,MAAM,qBAAqB,EAAE,MAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,uCAAuC,CAAC;AACtD,cAAc,wDAAwD,CAAC;AACvE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iBAAiB,CAAC;AAChC,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,YAAY,CAAC;AAE3B,eAAO,MAAM,qBAAqB,EAAE,MAAwB,CAAC"}
@@ -30,9 +30,12 @@ exports.DYE2E_RUNTIME_VERSION = void 0;
30
30
  * ```
31
31
  */
32
32
  __exportStar(require("./_collections/assertion-helpers.util"), exports);
33
+ __exportStar(require("./_collections/notification-detector-page-script.const"), exports);
34
+ __exportStar(require("./_collections/notification-detector.util"), exports);
33
35
  __exportStar(require("./_collections/unique-generator.util"), exports);
34
36
  __exportStar(require("./_collections/wait-helpers.util"), exports);
35
37
  __exportStar(require("./_models/index"), exports);
38
+ __exportStar(require("./_models/notification-report.interface"), exports);
36
39
  __exportStar(require("./_models/test-user.interface"), exports);
37
40
  __exportStar(require("./_services/dialog-page-object"), exports);
38
41
  __exportStar(require("./_services/field-page-object"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;GAaG;AACH,wEAAsD;AACtD,uEAAqD;AACrD,mEAAiD;AACjD,kDAAgC;AAChC,gEAA8C;AAC9C,iEAA+C;AAC/C,gEAA8C;AAC9C,+DAA6C;AAC7C,iEAA+C;AAC/C,6DAA2C;AAC3C,qEAAmD;AACnD,8DAA4C;AAC5C,gEAA8C;AAC9C,mEAAiD;AACjD,6CAA2B;AAEd,QAAA,qBAAqB,GAAW,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;GAaG;AACH,wEAAsD;AACtD,yFAAuE;AACvE,4EAA0D;AAC1D,uEAAqD;AACrD,mEAAiD;AACjD,kDAAgC;AAChC,0EAAwD;AACxD,gEAA8C;AAC9C,iEAA+C;AAC/C,gEAA8C;AAC9C,+DAA6C;AAC7C,iEAA+C;AAC/C,6DAA2C;AAC3C,qEAAmD;AACnD,8DAA4C;AAC5C,gEAA8C;AAC9C,mEAAiD;AACjD,6CAA2B;AAEd,QAAA,qBAAqB,GAAW,eAAe,CAAC"}
package/build/index.js CHANGED
@@ -24,5 +24,5 @@ __exportStar(require("./_modules/cwv/index"), exports);
24
24
  __exportStar(require("./_modules/vqa/index"), exports);
25
25
  __exportStar(require("./_modules/game/index"), exports);
26
26
  /** Package-version stamp — npm-publish-time runtime-érték. */
27
- exports.DYE2E_PACKAGE_VERSION = '01.18.13';
27
+ exports.DYE2E_PACKAGE_VERSION = '01.18.15';
28
28
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futdevpro/dynamo-e2e",
3
- "version": "01.18.13",
3
+ "version": "01.18.15",
4
4
  "description": "End-to-end testing engine for the Dynamo + FDP ecosystem — auto-generated form-coverage tests, viewport audit matrix, CWV diagnostics. Settings-POJO-first.",
5
5
  "DyBu_settings": {
6
6
  "packageType": "core-package",