@futdevpro/dynamo-e2e 1.18.38 → 1.18.40

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 (56) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +30 -0
  3. package/build/_modules/visual-gate/_collections/blocking-overlays.util.d.ts +63 -0
  4. package/build/_modules/visual-gate/_collections/blocking-overlays.util.d.ts.map +1 -0
  5. package/build/_modules/visual-gate/_collections/blocking-overlays.util.js +104 -0
  6. package/build/_modules/visual-gate/_collections/blocking-overlays.util.js.map +1 -0
  7. package/build/_modules/visual-gate/_collections/visual-gate-defaults.const.d.ts +23 -0
  8. package/build/_modules/visual-gate/_collections/visual-gate-defaults.const.d.ts.map +1 -0
  9. package/build/_modules/visual-gate/_collections/visual-gate-defaults.const.js +49 -0
  10. package/build/_modules/visual-gate/_collections/visual-gate-defaults.const.js.map +1 -0
  11. package/build/_modules/visual-gate/_collections/visual-gate-look.util.d.ts +51 -0
  12. package/build/_modules/visual-gate/_collections/visual-gate-look.util.d.ts.map +1 -0
  13. package/build/_modules/visual-gate/_collections/visual-gate-look.util.js +241 -0
  14. package/build/_modules/visual-gate/_collections/visual-gate-look.util.js.map +1 -0
  15. package/build/_modules/visual-gate/_collections/visual-gate.util.d.ts +84 -0
  16. package/build/_modules/visual-gate/_collections/visual-gate.util.d.ts.map +1 -0
  17. package/build/_modules/visual-gate/_collections/visual-gate.util.js +209 -0
  18. package/build/_modules/visual-gate/_collections/visual-gate.util.js.map +1 -0
  19. package/build/_modules/visual-gate/_models/visual-gate-case.interface.d.ts +14 -0
  20. package/build/_modules/visual-gate/_models/visual-gate-case.interface.d.ts.map +1 -0
  21. package/build/_modules/visual-gate/_models/visual-gate-case.interface.js +3 -0
  22. package/build/_modules/visual-gate/_models/visual-gate-case.interface.js.map +1 -0
  23. package/build/_modules/visual-gate/_models/visual-gate-finding.interface.d.ts +16 -0
  24. package/build/_modules/visual-gate/_models/visual-gate-finding.interface.d.ts.map +1 -0
  25. package/build/_modules/visual-gate/_models/visual-gate-finding.interface.js +3 -0
  26. package/build/_modules/visual-gate/_models/visual-gate-finding.interface.js.map +1 -0
  27. package/build/_modules/visual-gate/_models/visual-gate-language.interface.d.ts +8 -0
  28. package/build/_modules/visual-gate/_models/visual-gate-language.interface.d.ts.map +1 -0
  29. package/build/_modules/visual-gate/_models/visual-gate-language.interface.js +3 -0
  30. package/build/_modules/visual-gate/_models/visual-gate-language.interface.js.map +1 -0
  31. package/build/_modules/visual-gate/_models/visual-gate-layout.interface.d.ts +14 -0
  32. package/build/_modules/visual-gate/_models/visual-gate-layout.interface.d.ts.map +1 -0
  33. package/build/_modules/visual-gate/_models/visual-gate-layout.interface.js +3 -0
  34. package/build/_modules/visual-gate/_models/visual-gate-layout.interface.js.map +1 -0
  35. package/build/_modules/visual-gate/_models/visual-gate-look-options.interface.d.ts +33 -0
  36. package/build/_modules/visual-gate/_models/visual-gate-look-options.interface.d.ts.map +1 -0
  37. package/build/_modules/visual-gate/_models/visual-gate-look-options.interface.js +3 -0
  38. package/build/_modules/visual-gate/_models/visual-gate-look-options.interface.js.map +1 -0
  39. package/build/_modules/visual-gate/_models/visual-gate-options.interface.d.ts +32 -0
  40. package/build/_modules/visual-gate/_models/visual-gate-options.interface.d.ts.map +1 -0
  41. package/build/_modules/visual-gate/_models/visual-gate-options.interface.js +3 -0
  42. package/build/_modules/visual-gate/_models/visual-gate-options.interface.js.map +1 -0
  43. package/build/_modules/visual-gate/_models/visual-gate-page.interface.d.ts +15 -0
  44. package/build/_modules/visual-gate/_models/visual-gate-page.interface.d.ts.map +1 -0
  45. package/build/_modules/visual-gate/_models/visual-gate-page.interface.js +3 -0
  46. package/build/_modules/visual-gate/_models/visual-gate-page.interface.js.map +1 -0
  47. package/build/_modules/visual-gate/_models/visual-gate-viewport.interface.d.ts +8 -0
  48. package/build/_modules/visual-gate/_models/visual-gate-viewport.interface.d.ts.map +1 -0
  49. package/build/_modules/visual-gate/_models/visual-gate-viewport.interface.js +3 -0
  50. package/build/_modules/visual-gate/_models/visual-gate-viewport.interface.js.map +1 -0
  51. package/build/_modules/visual-gate/index.d.ts +19 -0
  52. package/build/_modules/visual-gate/index.d.ts.map +1 -0
  53. package/build/_modules/visual-gate/index.js +35 -0
  54. package/build/_modules/visual-gate/index.js.map +1 -0
  55. package/build/index.js +1 -1
  56. package/package.json +10 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,55 @@
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.40] — 2026-11-04 — `/visual-gate`: the look — neutral grey, text, production font sizes (BFR-FUTDEVPRO-081)
7
+
8
+ - **Why** (owner, 2026-11-02 21:43: "we are going backwards while we prepare for the release"; futdevpro
9
+ `BFR-FUTDEVPRO-081`): four defects the user saw reached the futdevpro TEST with no test failing — blue / green frames
10
+ instead of the neutral grey, a page "not found", tiles fallen apart, an admin list in one line. futdevpro's own gate
11
+ (`e2e/tests/ui-check/visual-regression-gate-feature.spec.ts`, 20 red on TEST 839, green on the fixed build) is now a
12
+ shared, parameterised tool: one call per product, its key pages, its content area, its reference URL, its waivers.
13
+ - **`DyE2E_VisualGateLook_Util`** (`DyE2E_VisualGate_Util.inspect` runs it): ① `not-found` (the product's "not found"
14
+ screen, `notFoundSelector`) and `content-empty` (the content area missing or lower than 150 px, `contentSelector`);
15
+ ③ `loud-color` — a box of the content area with a saturated (> 0.35) frame, background or gradient in the green …
16
+ blue range (80–260°), in any colour space (Tailwind 4 reports `oklch`: judged by its rendered sRGB pixel); red and
17
+ amber carry meaning and stay; buttons, fields, icons, Material and media are the shared
18
+ components and are skipped (`neutralSkipSelector`), a product's own decoration can be allowed
19
+ (`gradientAllowSelector`); ⑤ `text-off-screen` (a visible text beyond the screen edge even when an `overflow:
20
+ hidden` parent hides the scroll; a honeypot far off the screen is not), ⑥ `raw-icon-word` (a `mat-icon` showing
21
+ its ligature word), ⑦ `placeholder-text` ("coming soon", "hamarosan", "lorem ipsum", `TODO`, `TBD`). Every check can
22
+ be turned off or tuned; the defaults are futdevpro's measured values.
23
+ - **④ the production font sizes**: `assertPage(page, gateCase, { referenceUrl })` opens the page marked
24
+ `isOnReference` on production (read only, same screen size and language) and fails when a heading or paragraph size
25
+ of production is gone (`compareFontSizes`; a new size only on TEST is not a finding).
26
+ - **The report**: with `testInfo`, the full-page screenshot of the page (and of the reference) is attached before the
27
+ verdict, so a human sees it even when the case is red. `isScreenshotComparisonSkipped` turns the pixel comparison
28
+ off (a baseline taken on a developer machine renders the fonts differently; futdevpro's choice).
29
+ - Compatible: without the new options a 01.18.39 call checks the same, plus the neutral look and the text on the whole
30
+ page.
31
+ - 6 specs (4 on a real Chromium, fixture pages with each defect beside its fixed twin), 4 mutants killed; 626 / 0.
32
+
33
+ ## [01.18.39] — 2026-11-02 — `/visual-gate`: the visual regression gate, first slice (R1-72 d)
34
+
35
+ - **Why** (owner, 2026-11-02 21:39: the credit service's login "badly out of shape", "a review tool is needed";
36
+ `IN-2026-11-02-R1-72` (d)): the key pages, HU + EN, 1440 + 390, red in the CI when a page falls apart, disappears
37
+ or changes. The products wire it in.
38
+ - **`DyE2E_BlockingOverlays_Util`**: closes the two overlays of a TEST deployment through the app's own buttons
39
+ (the ngx-dynamo storage service writes a control record and may encrypt, so a hand-made `localStorage` value is
40
+ not reliably valid): the TEST warning by its panel class (`.dynx-test-ribbon-warning-dialog`; every "do not show
41
+ again" box ticked, so the ribbon is not in the screenshot either), the cookie bar by its test id (necessary
42
+ only). Language-neutral hooks. `prepareStorageState({ browser, baseUrl, locale })` returns the state to start
43
+ every case from, once per language; it fails loudly when an overlay is still visible after closing it.
44
+ Nothing to close (production) is not an error.
45
+ - **`DyE2E_VisualGate_Util`**: `cases({ pages })` (page × language × viewport, ids `<page>-<lang>-<viewport>`,
46
+ duplicate names refused); `inspect(page)` — an overlay still covering the page, `page-empty` (fewer than 20
47
+ visible characters or 5 visible elements), `horizontal-overflow` (the widest element named); `assertPage(page,
48
+ case)` — those first, with their reason, then `toHaveScreenshot(<id>.png)` (full page, animations off, masks,
49
+ 2 % tolerance).
50
+ - **Measured live** on the credit TEST `/login` (the real ngx-dynamo dialog + cookie bar): 4 / 4 cases closed both
51
+ overlays (the ribbon hidden too), HU and EN by the locale, no finding. ⚠️ A layout like the credit login before
52
+ ngx-fdp-templates 20.20.292 (a form across the whole page) is caught only by the baseline comparison, not by
53
+ the cheap checks.
54
+ - 7 specs (4 on a real Chromium, fixture pages), 4 mutants killed; 620 / 0.
6
55
  ## [01.18.38] — 2026-11-01 — `/ui-check`: `error-destination`, a press that lands on an error (BFR-FLEET-012 ②)
7
56
 
8
57
  - **`error-destination`** (`press-leads-to-error`; owner, 2026-10-26, `IN-2026-10-26-ORC-02`: "…ha valamire
package/README.md CHANGED
@@ -84,6 +84,36 @@ const report = await DyE2E_UICheckRunner_Util.run(page, { url: page.url(), accou
84
84
  DyE2E_UICheckRunner_Util.assertClean(report); // throws with every finding's measurement
85
85
  ```
86
86
 
87
+ - `@futdevpro/dynamo-e2e/visual-gate` — the visual regression gate: closes the TEST warning and the cookie bar through
88
+ the app (`DyE2E_BlockingOverlays_Util.prepareStorageState`, once per language), then checks each key page in
89
+ HU + EN at 1440 + 390 (`DyE2E_VisualGate_Util.cases`): red when an overlay still covers it, it shows (almost)
90
+ nothing, it is wider than the screen, or it differs from the committed baseline (`toHaveScreenshot`). The look
91
+ (`DyE2E_VisualGateLook_Util`, BFR-FUTDEVPRO-081): the "not found" screen, an empty content area, green / cyan / blue
92
+ instead of the neutral grey, a text off the screen, a raw icon word, a placeholder; with a `referenceUrl`, the
93
+ heading and paragraph sizes of production on the pages marked `isOnReference`. With `testInfo`, the screenshots
94
+ (TEST and production) go into the report.
95
+
96
+ ```ts
97
+ import { DyE2E_BlockingOverlays_Util, DyE2E_VisualGate_Util } from '@futdevpro/dynamo-e2e/visual-gate';
98
+
99
+ for (const gateCase of DyE2E_VisualGate_Util.cases({ pages: [ { name: 'login', path: '/login' } ] })) {
100
+ test(`| visual: ${gateCase.id}`, async ({ browser }) => {
101
+ const { storageState } = await DyE2E_BlockingOverlays_Util.prepareStorageState({
102
+ browser, baseUrl: BASE_URL, locale: gateCase.language.locale,
103
+ });
104
+ const context = await browser.newContext({ storageState, locale: gateCase.language.locale,
105
+ viewport: { width: gateCase.viewport.width, height: gateCase.viewport.height } });
106
+ const page = await context.newPage();
107
+
108
+ await page.goto(BASE_URL + gateCase.page.path);
109
+ await DyE2E_VisualGate_Util.assertPage(page, gateCase, {
110
+ contentSelector: 'main', notFoundSelector: 'app-a-block-message-screen',
111
+ referenceUrl: 'https://example.hu', testInfo: test.info(),
112
+ }); // layout + look first, then the production font sizes, then the screenshot comparison
113
+ });
114
+ }
115
+ ```
116
+
87
117
  ## Playwright preset — CI timeouts and the failure budget
88
118
 
89
119
  ```ts
@@ -0,0 +1,63 @@
1
+ import { Browser, BrowserContext, Page } from '@playwright/test';
2
+ /** What `DyE2E_BlockingOverlays_Util.acknowledge` closed. */
3
+ export interface DyE2E_BlockingOverlaysResult_Interface {
4
+ /** The TEST-environment warning was there and is acknowledged (every "do not show again" box ticked). */
5
+ isTestWarningAcknowledged: boolean;
6
+ /** The cookie bar was there and is decided (necessary cookies only). */
7
+ isCookieConsentDecided: boolean;
8
+ }
9
+ /** The browser state after the overlays are closed: reuse it as `storageState` of every visual-gate context. */
10
+ export type DyE2E_BlockingOverlaysState_Type = Awaited<ReturnType<BrowserContext['storageState']>>;
11
+ /**
12
+ * Closes the two overlays that cover every page of a TEST deployment before an automated check (R1-72 d): the
13
+ * TEST-environment warning (`dynamo-test-ribbon` of ngx-dynamo) and the cookie bar (`dynamo-cookie-consent`).
14
+ *
15
+ * @description Through the app itself, by its buttons — not by writing `localStorage` by hand: the ngx-dynamo storage
16
+ * service writes its own control record (`dynamo-control`) and may encrypt, so a hand-made value is not reliably
17
+ * valid (measured in futdevpro's `test-warning.global-setup.ts`). The hooks do not depend on the language: the
18
+ * warning dialog's own panel class, the cookie bar's `data-testid` (a test id built from a button caption changed
19
+ * when the caption was localised and silently broke a harness; FAM, adventor 2026-09-21).
20
+ */
21
+ export declare class DyE2E_BlockingOverlays_Util {
22
+ /** The TEST-warning dialog (its `panelClass`). */
23
+ static readonly testWarningSelector: string;
24
+ /** The cookie bar. */
25
+ static readonly cookieConsentSelector: string;
26
+ /** The cookie bar's "necessary only" button (the summary view and the details view both carry it). */
27
+ static readonly cookieRejectSelector: string;
28
+ /** How often the page is looked at while waiting for an overlay, ms. */
29
+ static readonly lookIntervalMs: number;
30
+ /**
31
+ * Closes the TEST warning and the cookie bar on this page, in the order they cover each other (the warning first).
32
+ *
33
+ * @description Waits up to `timeoutMs` for both: either may render late (the cookie bar waits for the language
34
+ * map). On production neither exists — that is not an error, the result says so.
35
+ */
36
+ static acknowledge(page: Page, timeoutMs?: number): Promise<DyE2E_BlockingOverlaysResult_Interface>;
37
+ /**
38
+ * The overlays that still cover the page (empty when the page is free).
39
+ *
40
+ * @returns the names: `test-warning`, `cookie-consent`
41
+ */
42
+ static findVisible(page: Page): Promise<string[]>;
43
+ /**
44
+ * Opens the app once in a fresh context, closes both overlays, and returns the browser state to start every
45
+ * visual-gate context from (`browser.newContext({ storageState })`).
46
+ *
47
+ * @description Call it once per language with that language's `locale`: an app that stores the language it
48
+ * picked would otherwise carry the first language into every case. Fails loudly when an overlay is still visible
49
+ * after closing it (a silent "nothing to close" hid a broken harness before; FAM, adventor 2026-09-21).
50
+ */
51
+ static prepareStorageState(set: {
52
+ browser: Browser;
53
+ baseUrl: string;
54
+ locale?: string;
55
+ timeoutMs?: number;
56
+ }): Promise<{
57
+ storageState: DyE2E_BlockingOverlaysState_Type;
58
+ result: DyE2E_BlockingOverlaysResult_Interface;
59
+ }>;
60
+ /** Ticks every "do not show again" box of the warning (the ribbon too, so it is not in the screenshot), confirms. */
61
+ private static acknowledgeTestWarning;
62
+ }
63
+ //# sourceMappingURL=blocking-overlays.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocking-overlays.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/visual-gate/_collections/blocking-overlays.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAW,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI1E,6DAA6D;AAC7D,MAAM,WAAW,sCAAsC;IACrD,yGAAyG;IACzG,yBAAyB,EAAE,OAAO,CAAC;IACnC,wEAAwE;IACxE,sBAAsB,EAAE,OAAO,CAAC;CACjC;AAED,gHAAgH;AAChH,MAAM,MAAM,gCAAgC,GAAG,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAEnG;;;;;;;;;GASG;AACH,qBAAa,2BAA2B;IAEtC,kDAAkD;IAClD,MAAM,CAAC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAsC;IAEjF,sBAAsB;IACtB,MAAM,CAAC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAoC;IAEjF,sGAAsG;IACtG,MAAM,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAA2C;IAEvF,wEAAwE;IACxE,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAO;IAE7C;;;;;OAKG;WACU,WAAW,CACtB,IAAI,EAAE,IAAI,EACV,SAAS,GAAE,MAAwD,GAClE,OAAO,CAAC,sCAAsC,CAAC;IAyBlD;;;;OAIG;WACU,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAavD;;;;;;;OAOG;WACU,mBAAmB,CAAC,GAAG,EAAE;QACpC,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,gCAAgC,CAAC;QAAC,MAAM,EAAE,sCAAsC,CAAA;KAAE,CAAC;IAsB/G,qHAAqH;mBAChG,sBAAsB;CAU5C"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DyE2E_BlockingOverlays_Util = void 0;
4
+ const visual_gate_defaults_const_1 = require("./visual-gate-defaults.const");
5
+ /**
6
+ * Closes the two overlays that cover every page of a TEST deployment before an automated check (R1-72 d): the
7
+ * TEST-environment warning (`dynamo-test-ribbon` of ngx-dynamo) and the cookie bar (`dynamo-cookie-consent`).
8
+ *
9
+ * @description Through the app itself, by its buttons — not by writing `localStorage` by hand: the ngx-dynamo storage
10
+ * service writes its own control record (`dynamo-control`) and may encrypt, so a hand-made value is not reliably
11
+ * valid (measured in futdevpro's `test-warning.global-setup.ts`). The hooks do not depend on the language: the
12
+ * warning dialog's own panel class, the cookie bar's `data-testid` (a test id built from a button caption changed
13
+ * when the caption was localised and silently broke a harness; FAM, adventor 2026-09-21).
14
+ */
15
+ class DyE2E_BlockingOverlays_Util {
16
+ /** The TEST-warning dialog (its `panelClass`). */
17
+ static testWarningSelector = '.dynx-test-ribbon-warning-dialog';
18
+ /** The cookie bar. */
19
+ static cookieConsentSelector = '[data-testid="cookie-consent"]';
20
+ /** The cookie bar's "necessary only" button (the summary view and the details view both carry it). */
21
+ static cookieRejectSelector = '[data-testid="cookie-consent-reject"]';
22
+ /** How often the page is looked at while waiting for an overlay, ms. */
23
+ static lookIntervalMs = 250;
24
+ /**
25
+ * Closes the TEST warning and the cookie bar on this page, in the order they cover each other (the warning first).
26
+ *
27
+ * @description Waits up to `timeoutMs` for both: either may render late (the cookie bar waits for the language
28
+ * map). On production neither exists — that is not an error, the result says so.
29
+ */
30
+ static async acknowledge(page, timeoutMs = visual_gate_defaults_const_1.DyE2E_VisualGateDefaults_Const.overlayTimeoutMs) {
31
+ const result = {
32
+ isTestWarningAcknowledged: false, isCookieConsentDecided: false,
33
+ };
34
+ const deadline = Date.now() + timeoutMs;
35
+ while (Date.now() < deadline && !(result.isTestWarningAcknowledged && result.isCookieConsentDecided)) {
36
+ const warning = page.locator(this.testWarningSelector).first();
37
+ const reject = page.locator(`${this.cookieRejectSelector}:visible`).first();
38
+ if (!result.isTestWarningAcknowledged && await warning.isVisible()) {
39
+ await this.acknowledgeTestWarning(warning);
40
+ result.isTestWarningAcknowledged = true;
41
+ }
42
+ else if (!result.isCookieConsentDecided && await reject.isVisible()) {
43
+ await reject.click();
44
+ await page.locator(this.cookieConsentSelector).first().waitFor({ state: 'hidden', timeout: 5_000 });
45
+ result.isCookieConsentDecided = true;
46
+ }
47
+ else {
48
+ await page.waitForTimeout(this.lookIntervalMs);
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+ /**
54
+ * The overlays that still cover the page (empty when the page is free).
55
+ *
56
+ * @returns the names: `test-warning`, `cookie-consent`
57
+ */
58
+ static async findVisible(page) {
59
+ const visible = [];
60
+ if (await page.locator(this.testWarningSelector).first().isVisible()) {
61
+ visible.push('test-warning');
62
+ }
63
+ if (await page.locator(this.cookieConsentSelector).first().isVisible()) {
64
+ visible.push('cookie-consent');
65
+ }
66
+ return visible;
67
+ }
68
+ /**
69
+ * Opens the app once in a fresh context, closes both overlays, and returns the browser state to start every
70
+ * visual-gate context from (`browser.newContext({ storageState })`).
71
+ *
72
+ * @description Call it once per language with that language's `locale`: an app that stores the language it
73
+ * picked would otherwise carry the first language into every case. Fails loudly when an overlay is still visible
74
+ * after closing it (a silent "nothing to close" hid a broken harness before; FAM, adventor 2026-09-21).
75
+ */
76
+ static async prepareStorageState(set) {
77
+ const context = await set.browser.newContext({ locale: set.locale, ignoreHTTPSErrors: true });
78
+ try {
79
+ const page = await context.newPage();
80
+ await page.goto(set.baseUrl, { waitUntil: 'domcontentloaded' });
81
+ const result = await this.acknowledge(page, set.timeoutMs);
82
+ const stillVisible = await this.findVisible(page);
83
+ if (stillVisible.length) {
84
+ throw new Error(`[dynamo-e2e visual-gate] still covering the page after closing: ${stillVisible.join(', ')} (${set.baseUrl})`);
85
+ }
86
+ return { storageState: await context.storageState(), result: result };
87
+ }
88
+ finally {
89
+ await context.close();
90
+ }
91
+ }
92
+ /** Ticks every "do not show again" box of the warning (the ribbon too, so it is not in the screenshot), confirms. */
93
+ static async acknowledgeTestWarning(warning) {
94
+ const boxes = warning.locator('input[type="checkbox"]');
95
+ const count = await boxes.count();
96
+ for (let i = 0; i < count; i++) {
97
+ await boxes.nth(i).check();
98
+ }
99
+ await warning.locator('button').last().click();
100
+ await warning.waitFor({ state: 'hidden', timeout: 5_000 });
101
+ }
102
+ }
103
+ exports.DyE2E_BlockingOverlays_Util = DyE2E_BlockingOverlays_Util;
104
+ //# sourceMappingURL=blocking-overlays.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocking-overlays.util.js","sourceRoot":"","sources":["../../../../src/_modules/visual-gate/_collections/blocking-overlays.util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { DyE2E_VisualGateLanguage_Interface } from '../_models/visual-gate-language.interface';
2
+ import { DyE2E_VisualGateViewport_Interface } from '../_models/visual-gate-viewport.interface';
3
+ /** The defaults of the visual gate (R1-72 d: "HU + EN, 1440 + 390"). */
4
+ export declare const DyE2E_VisualGateDefaults_Const: {
5
+ viewports: DyE2E_VisualGateViewport_Interface[];
6
+ languages: DyE2E_VisualGateLanguage_Interface[];
7
+ minVisibleTextLength: number;
8
+ minVisibleElementCount: number;
9
+ overflowTolerancePx: number;
10
+ maxDiffPixelRatio: number;
11
+ overlayTimeoutMs: number;
12
+ contentSelector: string;
13
+ minContentHeightPx: number;
14
+ neutralSkipSelector: string;
15
+ loudHueFrom: number;
16
+ loudHueTo: number;
17
+ loudMinSaturation: number;
18
+ loudMinAlpha: number;
19
+ placeholderPattern: string;
20
+ fontSizeTags: string[];
21
+ maxFindingsPerCheck: number;
22
+ };
23
+ //# sourceMappingURL=visual-gate-defaults.const.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-gate-defaults.const.d.ts","sourceRoot":"","sources":["../../../../src/_modules/visual-gate/_collections/visual-gate-defaults.const.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,2CAA2C,CAAC;AAC/F,OAAO,EAAE,kCAAkC,EAAE,MAAM,2CAA2C,CAAC;AAE/F,wEAAwE;AACxE,eAAO,MAAM,8BAA8B,EAAE;IAC3C,SAAS,EAAE,kCAAkC,EAAE,CAAC;IAChD,SAAS,EAAE,kCAAkC,EAAE,CAAC;IAChD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CA4C7B,CAAC"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DyE2E_VisualGateDefaults_Const = void 0;
4
+ /** The defaults of the visual gate (R1-72 d: "HU + EN, 1440 + 390"). */
5
+ exports.DyE2E_VisualGateDefaults_Const = {
6
+ viewports: [
7
+ { name: 'desktop', width: 1440, height: 900 },
8
+ { name: 'mobile', width: 390, height: 844 },
9
+ ],
10
+ languages: [
11
+ { code: 'hu', locale: 'hu-HU' },
12
+ { code: 'en', locale: 'en-US' },
13
+ ],
14
+ /** Fewer visible characters than this ⇒ the page disappeared (a heading and a button already give more). */
15
+ minVisibleTextLength: 20,
16
+ /** Fewer visible elements than this ⇒ the page disappeared. */
17
+ minVisibleElementCount: 5,
18
+ /** Sub-pixel rounding: a document at most this much wider than the viewport is not a sideways scroll. */
19
+ overflowTolerancePx: 1,
20
+ /** The share of differing pixels the screenshot comparison tolerates (antialiasing, a moved caret). */
21
+ maxDiffPixelRatio: 0.02,
22
+ /**
23
+ * How long the setup waits for the TEST warning and the cookie bar. Measured (FAM, futdevpro 2026-09-21): the bar
24
+ * renders behind the app's language map or a 4 s fail-open, so a slow runner needs well over 4 s.
25
+ */
26
+ overlayTimeoutMs: 15_000,
27
+ /** The content area when the product names none: the whole page. */
28
+ contentSelector: 'body',
29
+ /** A content area lower than this is empty (futdevpro's gate, BFR-FUTDEVPRO-081). */
30
+ minContentHeightPx: 150,
31
+ /**
32
+ * The shared components (their colours are the shared design's): buttons, fields, icons, Material, the notification
33
+ * area, media (futdevpro's gate, BFR-FUTDEVPRO-081).
34
+ */
35
+ neutralSkipSelector: 'button, dynamo-button-new, input, select, textarea, mat-icon, [class*="mat-"], '
36
+ + '[class*="mdc-"], dynamo-notifications-area, img, svg, video, canvas',
37
+ /** Green (80°) … blue (260°): the systems are neutral grey; red (an error) and amber (a to-do) carry meaning. */
38
+ loudHueFrom: 80,
39
+ loudHueTo: 260,
40
+ loudMinSaturation: 0.35,
41
+ loudMinAlpha: 0.15,
42
+ /** A finished page shows none of these (owner, 2026-11-02 22:27: "placeholders … as closed, finished items"). */
43
+ placeholderPattern: '(coming soon|hamarosan|lorem ipsum|\\bTODO\\b|\\bTBD\\b)',
44
+ /** The tags whose font sizes are compared with the reference. */
45
+ fontSizeTags: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'],
46
+ /** At most this many findings of one check are listed (the rest is the same defect). */
47
+ maxFindingsPerCheck: 12,
48
+ };
49
+ //# sourceMappingURL=visual-gate-defaults.const.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-gate-defaults.const.js","sourceRoot":"","sources":["../../../../src/_modules/visual-gate/_collections/visual-gate-defaults.const.ts"],"names":[],"mappings":";;;AAGA,wEAAwE;AAC3D,QAAA,8BAA8B,GAkBvC;IACF,SAAS,EAAE;QACT,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;QAC7C,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;KAC5C;IACD,SAAS,EAAE;QACT,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;KAChC;IACD,4GAA4G;IAC5G,oBAAoB,EAAE,EAAE;IACxB,+DAA+D;IAC/D,sBAAsB,EAAE,CAAC;IACzB,yGAAyG;IACzG,mBAAmB,EAAE,CAAC;IACtB,uGAAuG;IACvG,iBAAiB,EAAE,IAAI;IACvB;;;OAGG;IACH,gBAAgB,EAAE,MAAM;IACxB,oEAAoE;IACpE,eAAe,EAAE,MAAM;IACvB,qFAAqF;IACrF,kBAAkB,EAAE,GAAG;IACvB;;;OAGG;IACH,mBAAmB,EAAE,iFAAiF;UAClG,qEAAqE;IACzE,iHAAiH;IACjH,WAAW,EAAE,EAAE;IACf,SAAS,EAAE,GAAG;IACd,iBAAiB,EAAE,IAAI;IACvB,YAAY,EAAE,IAAI;IAClB,iHAAiH;IACjH,kBAAkB,EAAE,0DAA0D;IAC9E,iEAAiE;IACjE,YAAY,EAAE,CAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAE;IACzD,wFAAwF;IACxF,mBAAmB,EAAE,EAAE;CACxB,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { Page } from '@playwright/test';
2
+ import { DyE2E_VisualGateFinding_Interface } from '../_models/visual-gate-finding.interface';
3
+ import { DyE2E_VisualGateLookOptions_Interface } from '../_models/visual-gate-look-options.interface';
4
+ /** The font sizes per tag (`h1` … `p`), sorted and distinct, as computed (`16px`). */
5
+ export type DyE2E_VisualGateFontSizes_Type = Record<string, string[]>;
6
+ /**
7
+ * The look checks of the visual gate (BFR-FUTDEVPRO-081, owner 2026-11-02 21:43: "we are going backwards while we
8
+ * prepare for the release"): four defects reached the futdevpro TEST that no functional test noticed — blue / green
9
+ * frames instead of the neutral grey, a page "not found", tiles fallen apart, an admin list in one line.
10
+ *
11
+ * @description Ported from futdevpro's own gate (`e2e/tests/ui-check/visual-regression-gate-feature.spec.ts`, 20 red
12
+ * on TEST 839, green on the fixed build), parameterised: the content area, the "not found" screen, the shared
13
+ * components skipped and the loud hue range are options with futdevpro's values as defaults. Every check is a
14
+ * browser-side script (DOM APIs only), so it measures the rendered page, not the source.
15
+ */
16
+ export declare class DyE2E_VisualGateLook_Util {
17
+ /**
18
+ * ① the content area: `{ isNotFound, isContentMissing, contentHeight }`. Argument: `{ contentSelector,
19
+ * notFoundSelector }`.
20
+ */
21
+ static readonly contentScript: string;
22
+ /**
23
+ * ③ the neutral look: the boxes of the content area with a saturated frame, background or gradient in the loud
24
+ * hue range. Argument: the resolved look options. Returns `<tag.class> frame|background|gradient <colour>` lines.
25
+ */
26
+ static readonly loudColorsScript: string;
27
+ /**
28
+ * ⑤ ⑥ ⑦ on the whole page: a visible text off the screen, an icon showing its ligature word, a placeholder text.
29
+ * Argument: `{ placeholderPattern, maxFindings }`. Returns `{ kind, message }` findings.
30
+ */
31
+ static readonly textProblemsScript: string;
32
+ /** ④ the font sizes of the headings and paragraphs of the content area. Argument: `{ contentSelector, tags }`. */
33
+ static readonly fontSizesScript: string;
34
+ /** The options with every default filled in. */
35
+ static resolve(options?: DyE2E_VisualGateLookOptions_Interface): Required<DyE2E_VisualGateLookOptions_Interface>;
36
+ /**
37
+ * The look checks of one page: ① the "not found" screen and the content area (when the product names them),
38
+ * ③ the neutral look, ⑤ ⑥ ⑦ the text.
39
+ *
40
+ * @returns the findings; empty when the page looks as it should
41
+ */
42
+ static inspect(page: Page, options?: DyE2E_VisualGateLookOptions_Interface): Promise<DyE2E_VisualGateFinding_Interface[]>;
43
+ /** ④ the font sizes of the headings and paragraphs in the content area (after the fonts are loaded). */
44
+ static measureFontSizes(page: Page, contentSelector?: string): Promise<DyE2E_VisualGateFontSizes_Type>;
45
+ /**
46
+ * ④ judges the font sizes against the reference: a size the reference uses for a tag, missing here, is a finding
47
+ * ("the old font size dropped"). A size only here is not (a new heading may have its own).
48
+ */
49
+ static compareFontSizes(ours: DyE2E_VisualGateFontSizes_Type, reference: DyE2E_VisualGateFontSizes_Type): DyE2E_VisualGateFinding_Interface[];
50
+ }
51
+ //# sourceMappingURL=visual-gate-look.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-gate-look.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/visual-gate/_collections/visual-gate-look.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,OAAO,EAAE,iCAAiC,EAAE,MAAM,0CAA0C,CAAC;AAC7F,OAAO,EAAE,qCAAqC,EAAE,MAAM,+CAA+C,CAAC;AAGtG,sFAAsF;AACtF,MAAM,MAAM,8BAA8B,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAEtE;;;;;;;;;GASG;AACH,qBAAa,yBAAyB;IAEpC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAQlC;IAEH;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAuErC;IAEH;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAuCvC;IAEH,kHAAkH;IAClH,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAUpC;IAEH,gDAAgD;IAChD,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,qCAA0C,GAAG,QAAQ,CAAC,qCAAqC,CAAC;IAkBpH;;;;;OAKG;WACU,OAAO,CAClB,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,qCAA0C,GAClD,OAAO,CAAC,iCAAiC,EAAE,CAAC;IAqC/C,wGAAwG;WAC3F,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAS5G;;;OAGG;IACH,MAAM,CAAC,gBAAgB,CACrB,IAAI,EAAE,8BAA8B,EACpC,SAAS,EAAE,8BAA8B,GACxC,iCAAiC,EAAE;CAiBvC"}