@futdevpro/fdp-e2e-helpers 1.15.10 → 1.15.14

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.
@@ -0,0 +1,110 @@
1
+ import { Page } from '@playwright/test';
2
+ import { FDP_CWV_RunConfig_Interface } from '../_models/interfaces/fdp-cwv-run-config.interface';
3
+ import { FDP_CWV_RouteConfig_Interface } from '../_models/interfaces/fdp-cwv-route-config.interface';
4
+ import { FDP_CWV_Result_Interface } from '../_models/interfaces/fdp-cwv-result.interface';
5
+ import { FDP_CWV_RouteResult_Interface } from '../_models/interfaces/fdp-cwv-route-result.interface';
6
+ import { FDP_CWV_RawMetric_Interface } from '../_models/interfaces/fdp-cwv-raw-metric.interface';
7
+ import { FDP_CWV_Budgets_Interface } from '../_models/interfaces/fdp-cwv-budgets.interface';
8
+ /**
9
+ * FDP_CWV_Collector_Util — scriptelt, szintetikus Core Web Vitals diagnosztika
10
+ * Playwright + web-vitals/attribution alapon.
11
+ *
12
+ * Hasznalat (a fogyaszto e2e projektben, ahol a require.resolve megengedett):
13
+ * ```
14
+ * const wv = fs.readFileSync(require.resolve('web-vitals/dist/web-vitals.attribution.iife.js'), 'utf-8');
15
+ * const result = await FDP_CWV_Collector_Util.collect(page, { routes, webVitalsScript: wv, budgets });
16
+ * FDP_CWV_Collector_Util.writeResult(result, './logs/cicd-pipeline/diagnostics/cwv-diag.json');
17
+ * ```
18
+ *
19
+ * Mukodes route-onkent: (1) navigacio, (2) opcionalis interakcio az INP-hez,
20
+ * (3) KOTELEZO visibility-hidden flush (e nelkul az INP/CLS ures), (4) metrika-olvasas.
21
+ *
22
+ * Stateless static class — NO singleton-state, multi-call-safe.
23
+ */
24
+ export declare class FDP_CWV_Collector_Util {
25
+ /** web-vitals standard rating-kuszobok [good-felso, needs-improvement-felso]. */
26
+ static readonly THRESHOLDS: {
27
+ [key: string]: [number, number];
28
+ };
29
+ /**
30
+ * Egy teljes CWV diag-futas: minden konfiguralt route-ot megmer, es osszesitett
31
+ * tipizalt eredmenyt ad vissza. A `page`-et a hivo birtokolja (browser-lifecycle
32
+ * a Playwright test-frameworke); a collector csak vezerli.
33
+ */
34
+ static collect(page: Page, config: FDP_CWV_RunConfig_Interface): Promise<FDP_CWV_Result_Interface>;
35
+ /**
36
+ * Egy route nyers metrikaibol epit tipizalt route-eredmenyt + budget-kiertekeles.
37
+ */
38
+ static buildRouteResult(route: FDP_CWV_RouteConfig_Interface, label: string, raw: FDP_CWV_RawMetric_Interface[], budgets?: FDP_CWV_Budgets_Interface): FDP_CWV_RouteResult_Interface;
39
+ /**
40
+ * Pure budget-kiertekeles: mely metrikak vannak a konfiguralt budget FOLOTT.
41
+ * Unit-testelheto (nem igenyel bongeszot).
42
+ */
43
+ static evaluateBudgets(values: {
44
+ lcp?: number;
45
+ inp?: number;
46
+ cls?: number;
47
+ }, budgets?: FDP_CWV_Budgets_Interface): string[];
48
+ /**
49
+ * Pure rating-szamitas a web-vitals standard kuszobok szerint (fallback, ha a
50
+ * library `rating`-je hianyzik). Unit-testelheto.
51
+ */
52
+ static rate(name: string, value: number): 'good' | 'needs-improvement' | 'poor';
53
+ /**
54
+ * A legrosszabb (legnagyobb) ertekek az osszes route-on — trend-projekciohoz.
55
+ */
56
+ static summarizeWorst(routes: FDP_CWV_RouteResult_Interface[]): {
57
+ lcpMs?: number;
58
+ inpMs?: number;
59
+ cls?: number;
60
+ };
61
+ /**
62
+ * OPCIONALIS lokalis JSON artifact kiirasa (debug / archivalas). A CI-ban a
63
+ * kanonikus ut a `printStepResultMarker` (a step-eredmeny markerkent megy a
64
+ * report-ba) — ez a metodus csak akkor kell, ha kulon fajl-artifact is kell.
65
+ * Az envelope: `{ type:'cwv', name, status, summary, data:<FDP_CWV_Result> }`.
66
+ * Letrehozza a hianyzo konyvtarakat.
67
+ */
68
+ static writeResult(result: FDP_CWV_Result_Interface, outputPath: string): void;
69
+ /**
70
+ * Kibocsatja a generikus `[CDP_STEP_RESULT] {json}` marker-t a stdout-ra, amibol
71
+ * a `dc cdp` step-runner a step entry `message`/`detail` mezojebe teszi az
72
+ * eredmenyt — igy a CWV "csak az egyik step eredmenye" lesz, KULON diagnostics-
73
+ * koncepcio nelkul. A `message` egysoros osszegzes (-> CICD result step.message),
74
+ * a `detail` a teljes strukturalt result (-> cdpReport-ban utazik az Overseerig).
75
+ *
76
+ * EGYSOROS compact JSON (a step-runner soronkent olvas) — ezert a sajat sorat
77
+ * kapja, sose tordeld.
78
+ */
79
+ static printStepResultMarker(result: FDP_CWV_Result_Interface): void;
80
+ /**
81
+ * A `writeResult` altal kiirt envelope-fajlbol bocsatja ki a `[CDP_STEP_RESULT]`
82
+ * marker-t. CI-ban EZ a megbizhato ut: a e2e spec fajlba irja az eredmenyt
83
+ * (`writeResult`), majd a pipeline step a Playwright-futas UTAN egy `node` hivassal
84
+ * ezt meghivja — igy a marker KOZVETLENUL a step shell-stdout-jara kerul (NEM a
85
+ * Playwright reporter-en at, ami befolythatna/prefixelhetne a sort).
86
+ *
87
+ * Hasznalat a pipeline step parancsban:
88
+ * node -e "require('@futdevpro/fdp-e2e-helpers').FDP_CWV_Collector_Util.emitMarkerFromFile('cwv-result.json')"
89
+ */
90
+ static emitMarkerFromFile(filePath: string): void;
91
+ /**
92
+ * Egysoros, ember-olvashato osszegzes a CLI / Discord / dashboard renderhez.
93
+ * Pl. "3 route, worst LCP 3200ms, INP 250ms, CLS 0.050 (budget túllépés!)".
94
+ */
95
+ static buildSummaryLine(result: FDP_CWV_Result_Interface): string;
96
+ /**
97
+ * Origin (protocol+host) kinyerese egy URL-bol — robusztus malformed-re.
98
+ */
99
+ static extractOrigin(url: string): string;
100
+ /**
101
+ * Visibility-hidden + pagehide szimulacio — ez kenyszeríti ki a web-vitals
102
+ * vegso INP/CLS/LCP callback-jeit. A kovetkezo navigacio friss document-tel resetel.
103
+ */
104
+ private static forceVisibilityHidden;
105
+ /**
106
+ * Helper: error -> rovid uzenet (DyFM_Error / Error / string / unknown).
107
+ */
108
+ private static errMsg;
109
+ }
110
+ //# sourceMappingURL=fdp-cwv-collector.util.d.ts.map
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FDP_CWV_Collector_Util = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
8
+ /**
9
+ * Bongeszo-oldali SETUP — `page.addInitScript`-en at fut, MINDEN navigaciora ujra.
10
+ * Felteszi hogy a web-vitals/attribution IIFE mar definialta a `window.webVitals`-t
11
+ * (a collector ezt elobb injektalja). Regisztralja az onLCP/onINP/onCLS/onFCP/onTTFB
12
+ * callback-eket egy `window.__fdpCwv` gyujtore.
13
+ *
14
+ * FONTOS: standalone fuggveny (NEM hivatkozhat kulso scope-ra a `prefixes` argon kivul),
15
+ * mert a Playwright serializalja es a bongeszoben futtatja.
16
+ */
17
+ const FDP_CWV_browserSetup = (prefixes) => {
18
+ const w = window;
19
+ if (!w.webVitals) {
20
+ return;
21
+ }
22
+ w.__fdpCwv = [];
23
+ const store = w.__fdpCwv;
24
+ // generateTarget: a kivalto elemhez megkeresi a legkozelebbi FDP-komponens host-ot
25
+ // (olyan custom-element, aminek a tag-prefixe a `prefixes`-ben van), es elere fuzi.
26
+ const generateTarget = (node) => {
27
+ try {
28
+ const el = node;
29
+ if (!el || !el.tagName) {
30
+ return 'unknown';
31
+ }
32
+ let componentTag = '';
33
+ let walker = el;
34
+ while (walker && walker.tagName) {
35
+ const tag = walker.tagName.toLowerCase();
36
+ const isComponent = tag.indexOf('-') > -1 &&
37
+ (!prefixes.length || prefixes.some((p) => tag.indexOf(p) === 0));
38
+ if (isComponent) {
39
+ componentTag = tag;
40
+ break;
41
+ }
42
+ walker = walker.parentElement;
43
+ }
44
+ const base = el.tagName.toLowerCase();
45
+ const id = el.id ? `#${el.id}` : '';
46
+ const selector = `${base}${id}`;
47
+ return componentTag ? `${componentTag} >> ${selector}` : selector;
48
+ }
49
+ catch {
50
+ return 'unknown';
51
+ }
52
+ };
53
+ const opts = {
54
+ reportAllChanges: false,
55
+ generateTarget: generateTarget,
56
+ };
57
+ const push = (m) => {
58
+ store.push(m);
59
+ };
60
+ // LCP/INP/CLS kapja a generateTarget-et (van target-juk); FCP/TTFB nem.
61
+ w.webVitals.onLCP(push, opts);
62
+ w.webVitals.onINP(push, opts);
63
+ w.webVitals.onCLS(push, opts);
64
+ w.webVitals.onFCP(push);
65
+ w.webVitals.onTTFB(push);
66
+ };
67
+ /**
68
+ * Bongeszo-oldali READ — `page.evaluate`-en at. A nyers web-vitals metrikakbol
69
+ * kiemeli a serializalhato mezoket (a nyers PerformanceEntry-ket NEM viszi at).
70
+ * Az attribution mezoneveit allowlist-tel masolja (v3/v4-robusztus).
71
+ */
72
+ const FDP_CWV_browserRead = () => {
73
+ const w = window;
74
+ const raw = w.__fdpCwv || [];
75
+ const NUM_KEYS = [
76
+ 'timeToFirstByte', 'resourceLoadDelay', 'resourceLoadDuration', 'elementRenderDelay',
77
+ 'inputDelay', 'processingDuration', 'presentationDelay', 'largestShiftValue', 'largestShiftTime',
78
+ ];
79
+ const STR_KEYS = ['url', 'interactionType'];
80
+ const TARGET_KEYS = ['target', 'element', 'interactionTarget', 'largestShiftTarget'];
81
+ return raw.map((m) => {
82
+ const attrRaw = m['attribution'] || {};
83
+ const attribution = {};
84
+ for (const key of TARGET_KEYS) {
85
+ if (typeof attrRaw[key] === 'string' && attrRaw[key]) {
86
+ attribution['target'] = attrRaw[key];
87
+ break;
88
+ }
89
+ }
90
+ for (const key of STR_KEYS) {
91
+ if (typeof attrRaw[key] === 'string') {
92
+ attribution[key] = attrRaw[key];
93
+ }
94
+ }
95
+ for (const key of NUM_KEYS) {
96
+ if (typeof attrRaw[key] === 'number') {
97
+ attribution[key] = Math.round(attrRaw[key]);
98
+ }
99
+ }
100
+ return {
101
+ name: m['name'],
102
+ value: m['value'],
103
+ rating: m['rating'] || 'good',
104
+ attribution: attribution,
105
+ };
106
+ });
107
+ };
108
+ /**
109
+ * FDP_CWV_Collector_Util — scriptelt, szintetikus Core Web Vitals diagnosztika
110
+ * Playwright + web-vitals/attribution alapon.
111
+ *
112
+ * Hasznalat (a fogyaszto e2e projektben, ahol a require.resolve megengedett):
113
+ * ```
114
+ * const wv = fs.readFileSync(require.resolve('web-vitals/dist/web-vitals.attribution.iife.js'), 'utf-8');
115
+ * const result = await FDP_CWV_Collector_Util.collect(page, { routes, webVitalsScript: wv, budgets });
116
+ * FDP_CWV_Collector_Util.writeResult(result, './logs/cicd-pipeline/diagnostics/cwv-diag.json');
117
+ * ```
118
+ *
119
+ * Mukodes route-onkent: (1) navigacio, (2) opcionalis interakcio az INP-hez,
120
+ * (3) KOTELEZO visibility-hidden flush (e nelkul az INP/CLS ures), (4) metrika-olvasas.
121
+ *
122
+ * Stateless static class — NO singleton-state, multi-call-safe.
123
+ */
124
+ class FDP_CWV_Collector_Util {
125
+ /** web-vitals standard rating-kuszobok [good-felso, needs-improvement-felso]. */
126
+ static THRESHOLDS = {
127
+ LCP: [2500, 4000],
128
+ INP: [200, 500],
129
+ CLS: [0.1, 0.25],
130
+ FCP: [1800, 3000],
131
+ TTFB: [800, 1800],
132
+ };
133
+ /**
134
+ * Egy teljes CWV diag-futas: minden konfiguralt route-ot megmer, es osszesitett
135
+ * tipizalt eredmenyt ad vissza. A `page`-et a hivo birtokolja (browser-lifecycle
136
+ * a Playwright test-frameworke); a collector csak vezerli.
137
+ */
138
+ static async collect(page, config) {
139
+ const prefixes = config.componentPrefixes ?? [];
140
+ const flushTimeout = config.flushTimeoutMs ?? 600;
141
+ const navTimeout = config.navigationTimeoutMs ?? 20000;
142
+ // Egyszer regisztraljuk — mindketto MINDEN navigaciora ujra fut (web-vitals reset).
143
+ // A web-vitals IIFE `var webVitals=...`-t csinal; a Playwright addInitScript viszont
144
+ // FUGGVENY-SCOPE-ba wrappeli a scriptet, igy a top-level `var` NEM kerul a window-ra.
145
+ // Ezert ugyanabban a scope-ban (a var utan) explicit ra-attach-oljuk a global-ra.
146
+ const webVitalsInjection = `${config.webVitalsScript}\n;try{self.webVitals=self.webVitals||webVitals;}catch(e){}`;
147
+ await page.addInitScript(webVitalsInjection);
148
+ await page.addInitScript(FDP_CWV_browserSetup, prefixes);
149
+ const routeResults = [];
150
+ let baseUrl = '';
151
+ for (const route of config.routes) {
152
+ const label = route.label ?? route.path;
153
+ fsm_dynamo_1.DyFM_Log.info(`[FDP_CWV] Mérés: ${label} (${route.path})`);
154
+ await page.goto(route.path, { waitUntil: 'load', timeout: navTimeout });
155
+ if (!baseUrl) {
156
+ baseUrl = FDP_CWV_Collector_Util.extractOrigin(page.url());
157
+ }
158
+ await page.waitForLoadState('networkidle', { timeout: navTimeout }).catch(() => undefined);
159
+ if (route.waitForSelector) {
160
+ await page.waitForSelector(route.waitForSelector, { timeout: navTimeout }).catch(() => null);
161
+ }
162
+ if (route.interact) {
163
+ try {
164
+ await route.interact(page);
165
+ }
166
+ catch (error) {
167
+ fsm_dynamo_1.DyFM_Log.warn(`[FDP_CWV] interact() hiba a(z) ${label} route-on: ${FDP_CWV_Collector_Util.errMsg(error)}`);
168
+ }
169
+ }
170
+ // KOTELEZO flush: az onINP/onCLS/onLCP csak visibility-hidden-re adja a vegso erteket.
171
+ await FDP_CWV_Collector_Util.forceVisibilityHidden(page);
172
+ await page.waitForTimeout(flushTimeout);
173
+ const raw = await page.evaluate(FDP_CWV_browserRead);
174
+ routeResults.push(FDP_CWV_Collector_Util.buildRouteResult(route, label, raw, config.budgets));
175
+ }
176
+ const worst = FDP_CWV_Collector_Util.summarizeWorst(routeResults);
177
+ const anyOverBudget = routeResults.some((r) => r.overBudget.length > 0);
178
+ return {
179
+ collectedAt: new Date().toISOString(),
180
+ baseUrl: baseUrl,
181
+ budgets: config.budgets,
182
+ routes: routeResults,
183
+ worst: worst,
184
+ anyOverBudget: anyOverBudget,
185
+ status: anyOverBudget ? 'warn' : 'ok',
186
+ };
187
+ }
188
+ /**
189
+ * Egy route nyers metrikaibol epit tipizalt route-eredmenyt + budget-kiertekeles.
190
+ */
191
+ static buildRouteResult(route, label, raw, budgets) {
192
+ const byName = new Map();
193
+ for (const m of raw) {
194
+ byName.set(m.name, m); // last-wins (reportAllChanges:false eseten egy is van)
195
+ }
196
+ const toMetric = (m) => m ? { value: m.value, rating: m.rating, attribution: m.attribution } : undefined;
197
+ const lcp = toMetric(byName.get('LCP'));
198
+ const inp = toMetric(byName.get('INP'));
199
+ const cls = toMetric(byName.get('CLS'));
200
+ const fcp = toMetric(byName.get('FCP'));
201
+ const ttfb = toMetric(byName.get('TTFB'));
202
+ const overBudget = FDP_CWV_Collector_Util.evaluateBudgets({ lcp: lcp?.value, inp: inp?.value, cls: cls?.value }, budgets);
203
+ return {
204
+ route: route.path,
205
+ label: label,
206
+ lcp: lcp,
207
+ inp: inp,
208
+ cls: cls,
209
+ fcp: fcp,
210
+ ttfb: ttfb,
211
+ overBudget: overBudget,
212
+ };
213
+ }
214
+ /**
215
+ * Pure budget-kiertekeles: mely metrikak vannak a konfiguralt budget FOLOTT.
216
+ * Unit-testelheto (nem igenyel bongeszot).
217
+ */
218
+ static evaluateBudgets(values, budgets) {
219
+ const over = [];
220
+ if (!budgets) {
221
+ return over;
222
+ }
223
+ if (values.lcp != null && budgets.lcpMs != null && values.lcp > budgets.lcpMs) {
224
+ over.push('lcp');
225
+ }
226
+ if (values.inp != null && budgets.inpMs != null && values.inp > budgets.inpMs) {
227
+ over.push('inp');
228
+ }
229
+ if (values.cls != null && budgets.cls != null && values.cls > budgets.cls) {
230
+ over.push('cls');
231
+ }
232
+ return over;
233
+ }
234
+ /**
235
+ * Pure rating-szamitas a web-vitals standard kuszobok szerint (fallback, ha a
236
+ * library `rating`-je hianyzik). Unit-testelheto.
237
+ */
238
+ static rate(name, value) {
239
+ const t = FDP_CWV_Collector_Util.THRESHOLDS[name];
240
+ if (!t) {
241
+ return 'good';
242
+ }
243
+ if (value <= t[0]) {
244
+ return 'good';
245
+ }
246
+ if (value <= t[1]) {
247
+ return 'needs-improvement';
248
+ }
249
+ return 'poor';
250
+ }
251
+ /**
252
+ * A legrosszabb (legnagyobb) ertekek az osszes route-on — trend-projekciohoz.
253
+ */
254
+ static summarizeWorst(routes) {
255
+ const worst = {};
256
+ for (const r of routes) {
257
+ if (r.lcp) {
258
+ worst.lcpMs = Math.max(worst.lcpMs ?? 0, r.lcp.value);
259
+ }
260
+ if (r.inp) {
261
+ worst.inpMs = Math.max(worst.inpMs ?? 0, r.inp.value);
262
+ }
263
+ if (r.cls) {
264
+ worst.cls = Math.max(worst.cls ?? 0, r.cls.value);
265
+ }
266
+ }
267
+ return worst;
268
+ }
269
+ /**
270
+ * OPCIONALIS lokalis JSON artifact kiirasa (debug / archivalas). A CI-ban a
271
+ * kanonikus ut a `printStepResultMarker` (a step-eredmeny markerkent megy a
272
+ * report-ba) — ez a metodus csak akkor kell, ha kulon fajl-artifact is kell.
273
+ * Az envelope: `{ type:'cwv', name, status, summary, data:<FDP_CWV_Result> }`.
274
+ * Letrehozza a hianyzo konyvtarakat.
275
+ */
276
+ static writeResult(result, outputPath) {
277
+ const dir = path.dirname(outputPath);
278
+ fs.mkdirSync(dir, { recursive: true });
279
+ const envelope = {
280
+ type: 'cwv',
281
+ name: 'Core Web Vitals',
282
+ status: result.status,
283
+ summary: FDP_CWV_Collector_Util.buildSummaryLine(result),
284
+ data: result,
285
+ };
286
+ fs.writeFileSync(outputPath, JSON.stringify(envelope, null, 2), 'utf-8');
287
+ fsm_dynamo_1.DyFM_Log.info(`[FDP_CWV] Eredmény kiírva: ${outputPath} (status=${result.status})`);
288
+ }
289
+ /**
290
+ * Kibocsatja a generikus `[CDP_STEP_RESULT] {json}` marker-t a stdout-ra, amibol
291
+ * a `dc cdp` step-runner a step entry `message`/`detail` mezojebe teszi az
292
+ * eredmenyt — igy a CWV "csak az egyik step eredmenye" lesz, KULON diagnostics-
293
+ * koncepcio nelkul. A `message` egysoros osszegzes (-> CICD result step.message),
294
+ * a `detail` a teljes strukturalt result (-> cdpReport-ban utazik az Overseerig).
295
+ *
296
+ * EGYSOROS compact JSON (a step-runner soronkent olvas) — ezert a sajat sorat
297
+ * kapja, sose tordeld.
298
+ */
299
+ static printStepResultMarker(result) {
300
+ const payload = {
301
+ message: FDP_CWV_Collector_Util.buildSummaryLine(result),
302
+ detail: result,
303
+ };
304
+ // Sajat sor, compact JSON — a `[CDP_STEP_RESULT]` marker-regex egy soron varja.
305
+ process.stdout.write(`\n[CDP_STEP_RESULT] ${JSON.stringify(payload)}\n`);
306
+ }
307
+ /**
308
+ * A `writeResult` altal kiirt envelope-fajlbol bocsatja ki a `[CDP_STEP_RESULT]`
309
+ * marker-t. CI-ban EZ a megbizhato ut: a e2e spec fajlba irja az eredmenyt
310
+ * (`writeResult`), majd a pipeline step a Playwright-futas UTAN egy `node` hivassal
311
+ * ezt meghivja — igy a marker KOZVETLENUL a step shell-stdout-jara kerul (NEM a
312
+ * Playwright reporter-en at, ami befolythatna/prefixelhetne a sort).
313
+ *
314
+ * Hasznalat a pipeline step parancsban:
315
+ * node -e "require('@futdevpro/fdp-e2e-helpers').FDP_CWV_Collector_Util.emitMarkerFromFile('cwv-result.json')"
316
+ */
317
+ static emitMarkerFromFile(filePath) {
318
+ const raw = fs.readFileSync(filePath, 'utf-8');
319
+ const parsed = JSON.parse(raw);
320
+ // Envelope (`{ ..., data }`) vagy nyers result is elfogadott.
321
+ const result = (parsed.data ?? parsed);
322
+ FDP_CWV_Collector_Util.printStepResultMarker(result);
323
+ }
324
+ /**
325
+ * Egysoros, ember-olvashato osszegzes a CLI / Discord / dashboard renderhez.
326
+ * Pl. "3 route, worst LCP 3200ms, INP 250ms, CLS 0.050 (budget túllépés!)".
327
+ */
328
+ static buildSummaryLine(result) {
329
+ const parts = [`${result.routes.length} route`];
330
+ if (result.worst.lcpMs != null) {
331
+ parts.push(`worst LCP ${Math.round(result.worst.lcpMs)}ms`);
332
+ }
333
+ if (result.worst.inpMs != null) {
334
+ parts.push(`INP ${Math.round(result.worst.inpMs)}ms`);
335
+ }
336
+ if (result.worst.cls != null) {
337
+ parts.push(`CLS ${result.worst.cls.toFixed(3)}`);
338
+ }
339
+ if (result.anyOverBudget) {
340
+ parts.push('(budget túllépés!)');
341
+ }
342
+ return parts.join(', ');
343
+ }
344
+ /**
345
+ * Origin (protocol+host) kinyerese egy URL-bol — robusztus malformed-re.
346
+ */
347
+ static extractOrigin(url) {
348
+ try {
349
+ return new URL(url).origin;
350
+ }
351
+ catch {
352
+ return url;
353
+ }
354
+ }
355
+ /**
356
+ * Visibility-hidden + pagehide szimulacio — ez kenyszeríti ki a web-vitals
357
+ * vegso INP/CLS/LCP callback-jeit. A kovetkezo navigacio friss document-tel resetel.
358
+ */
359
+ static async forceVisibilityHidden(page) {
360
+ await page.evaluate(() => {
361
+ try {
362
+ Object.defineProperty(document, 'visibilityState', { configurable: true, get: () => 'hidden' });
363
+ Object.defineProperty(document, 'hidden', { configurable: true, get: () => true });
364
+ }
365
+ catch {
366
+ // ignore — ha mar redefinialt, a dispatch onmagaban is triggerel
367
+ }
368
+ document.dispatchEvent(new Event('visibilitychange'));
369
+ window.dispatchEvent(new Event('pagehide'));
370
+ });
371
+ }
372
+ /**
373
+ * Helper: error -> rovid uzenet (DyFM_Error / Error / string / unknown).
374
+ */
375
+ static errMsg(error) {
376
+ if (error instanceof Error) {
377
+ return error.message;
378
+ }
379
+ return typeof error === 'string' ? error : JSON.stringify(error ?? {}).substring(0, 200);
380
+ }
381
+ }
382
+ exports.FDP_CWV_Collector_Util = FDP_CWV_Collector_Util;
383
+ //# sourceMappingURL=fdp-cwv-collector.util.js.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * CWV attribution — a kiváltó ok beazonosításához.
3
+ *
4
+ * A `web-vitals/attribution` build adja: nem csak a metrika-értéket, hanem a
5
+ * kiváltó DOM-elemet (selector) + a fázis-breakdown-t. Minden mező opcionális,
6
+ * mert metrikánként más-más releváns (a collector csak a primitív/string mezőket
7
+ * emeli ki, hogy a result JSON-serializálható maradjon — nyers PerformanceEntry-t
8
+ * NEM viszünk át).
9
+ */
10
+ export interface FDP_CWV_Attribution_Interface {
11
+ /**
12
+ * A kiváltó elem selectora: LCP-nél a legnagyobb elem, INP-nél az interakció
13
+ * célpontja, CLS-nél a legtöbbet elmozduló elem. Ha a `componentPrefixes`
14
+ * konfigurálva van, komponens-szintű prefix-szel annotálva.
15
+ */
16
+ target?: string;
17
+ /** LCP: erőforrás URL-je (ha az LCP-elem kép). */
18
+ url?: string;
19
+ /** LCP fázis / FCP / TTFB: szerver-válaszidő ms. */
20
+ timeToFirstByte?: number;
21
+ /** LCP fázis: az LCP-erőforrás betöltés-késleltetése ms. */
22
+ resourceLoadDelay?: number;
23
+ /** LCP fázis: az LCP-erőforrás betöltési ideje ms. */
24
+ resourceLoadDuration?: number;
25
+ /** LCP fázis: render-késleltetés a betöltés után ms. */
26
+ elementRenderDelay?: number;
27
+ /** INP: az interakció típusa (pointer / keyboard). */
28
+ interactionType?: string;
29
+ /** INP fázis: input-késleltetés a fő szálon ms. */
30
+ inputDelay?: number;
31
+ /** INP fázis: event-handler feldolgozási idő ms (gyakran change-detection). */
32
+ processingDuration?: number;
33
+ /** INP fázis: a következő paint megjelenítési késleltetése ms. */
34
+ presentationDelay?: number;
35
+ /** CLS: a legnagyobb egyedi layout-shift értéke. */
36
+ largestShiftValue?: number;
37
+ /** CLS: a legnagyobb shift időpontja ms. */
38
+ largestShiftTime?: number;
39
+ }
40
+ //# sourceMappingURL=fdp-cwv-attribution.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-attribution.interface.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Core Web Vitals budget-küszöbök egy CWV diag-futáshoz.
3
+ *
4
+ * A budget NEM a Google standard rating-küszöb (azt a `web-vitals` library adja
5
+ * `metric.rating`-ben) — ez a MI konfigurálható kapunk: ha egy route metrikája
6
+ * rosszabb a budgetnél, a route `overBudget`-listájába kerül, és a teljes futás
7
+ * `status: 'warn'` lesz. POC-ban warn-only (nem blokkol deploy-t).
8
+ *
9
+ * Mértékegységek: lcpMs / inpMs milliszekundum, cls dimenzió-nélküli.
10
+ * Bármelyik mező opcionális — ha nincs megadva, az adott metrikára nincs budget-check.
11
+ */
12
+ export interface FDP_CWV_Budgets_Interface {
13
+ /** Largest Contentful Paint felső budget ms-ben (Google "good": 2500). */
14
+ lcpMs?: number;
15
+ /** Interaction to Next Paint felső budget ms-ben (Google "good": 200). */
16
+ inpMs?: number;
17
+ /** Cumulative Layout Shift felső budget (Google "good": 0.1). */
18
+ cls?: number;
19
+ }
20
+ //# sourceMappingURL=fdp-cwv-budgets.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-budgets.interface.js.map
@@ -0,0 +1,17 @@
1
+ import { FDP_CWV_Attribution_Interface } from './fdp-cwv-attribution.interface';
2
+ /**
3
+ * Egy CWV metrika mért értéke + minősítése + attribution-je.
4
+ *
5
+ * A `rating` a `web-vitals` library standard küszöbei szerint ('good' < küszöb1,
6
+ * 'needs-improvement' < küszöb2, 'poor' afölött). Az `overBudget` (a route-szinten)
7
+ * a MI konfigurálható budgetünk — a kettő független.
8
+ */
9
+ export interface FDP_CWV_Metric_Interface {
10
+ /** A mért érték (LCP/INP/FCP/TTFB ms-ben; CLS dimenzió-nélküli). */
11
+ value: number;
12
+ /** web-vitals standard minősítés. */
13
+ rating: 'good' | 'needs-improvement' | 'poor';
14
+ /** A kiváltó ok beazonosítása (DOM-elem + fázis-breakdown). */
15
+ attribution?: FDP_CWV_Attribution_Interface;
16
+ }
17
+ //# sourceMappingURL=fdp-cwv-metric.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-metric.interface.js.map
@@ -0,0 +1,18 @@
1
+ import { FDP_CWV_Attribution_Interface } from './fdp-cwv-attribution.interface';
2
+ /**
3
+ * Nyers metrika-alak, amit a collector bongeszo-oldali read-fuggvenye visszaad
4
+ * (mar serializalhato: primitivek + string-ek, nyers PerformanceEntry NELKUL).
5
+ *
6
+ * A `FDP_CWV_Collector_Util.buildRouteResult` ezt alakitja tipizalt route-eredmennye.
7
+ */
8
+ export interface FDP_CWV_RawMetric_Interface {
9
+ /** Metrika neve: 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB'. */
10
+ name: string;
11
+ /** A mert ertek. */
12
+ value: number;
13
+ /** web-vitals standard minosites. */
14
+ rating: 'good' | 'needs-improvement' | 'poor';
15
+ /** Kiemelt, serializalhato attribution-mezok. */
16
+ attribution: FDP_CWV_Attribution_Interface;
17
+ }
18
+ //# sourceMappingURL=fdp-cwv-raw-metric.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-raw-metric.interface.js.map
@@ -0,0 +1,31 @@
1
+ import { FDP_CWV_RouteResult_Interface } from './fdp-cwv-route-result.interface';
2
+ import { FDP_CWV_Budgets_Interface } from './fdp-cwv-budgets.interface';
3
+ /**
4
+ * Egy teljes CWV diag-futás eredménye — ez kerül a sidecar JSON-be
5
+ * (`./logs/cicd-pipeline/diagnostics/cwv-diag.json`), majd a `dc cdp`
6
+ * report-builder a `cicd-report.json` `diagnostics[].data`-jába emeli.
7
+ *
8
+ * `status`: 'ok' ha egyik route sem lépte túl a budgetet, 'warn' ha legalább egy
9
+ * túllépte. POC-ban a 'warn' NEM blokkolja a pipeline-t (report-only).
10
+ */
11
+ export interface FDP_CWV_Result_Interface {
12
+ /** A mérés időpontja ISO-stringként. */
13
+ collectedAt: string;
14
+ /** A mért base URL (az első navigáció origin-je). */
15
+ baseUrl: string;
16
+ /** Az alkalmazott budgetek (visszajátszáshoz / riporthoz). */
17
+ budgets?: FDP_CWV_Budgets_Interface;
18
+ /** Route-onkénti eredmények. */
19
+ routes: FDP_CWV_RouteResult_Interface[];
20
+ /** A legrosszabb értékek az összes route-on (trend-projekcióhoz az Overseerben). */
21
+ worst: {
22
+ lcpMs?: number;
23
+ inpMs?: number;
24
+ cls?: number;
25
+ };
26
+ /** Igaz, ha legalább egy route legalább egy metrikája budget fölött van. */
27
+ anyOverBudget: boolean;
28
+ /** Összesített státusz: 'ok' | 'warn'. */
29
+ status: 'ok' | 'warn';
30
+ }
31
+ //# sourceMappingURL=fdp-cwv-result.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-result.interface.js.map
@@ -0,0 +1,26 @@
1
+ import { Page } from '@playwright/test';
2
+ /**
3
+ * Egy mérendő route konfigurációja a CWV collector-hez.
4
+ *
5
+ * A `path` relatív (a Playwright `page.goto` a context `baseURL`-jéhez fűzi), így
6
+ * ugyanaz a route-lista lokál (`http://localhost:4212`) és deployolt test-env
7
+ * (`https://test.organizer...`) ellen is használható.
8
+ *
9
+ * Az `interact` callback a felelős az INP kiváltásáért: VALÓS Playwright input
10
+ * (click/type/scroll) kell — szintetikus event-dispatch NEM generál INP-t.
11
+ */
12
+ export interface FDP_CWV_RouteConfig_Interface {
13
+ /** Relatív route path, pl. '/landing/login' vagy '/main/home'. */
14
+ path: string;
15
+ /** Olvasható címke a riportban; ha hiányzik, a `path` használt. */
16
+ label?: string;
17
+ /** Opcionális selector, amire navigáció után várunk (oldal-kész jelzés). */
18
+ waitForSelector?: string;
19
+ /**
20
+ * Opcionális interakció-script az INP méréséhez. VALÓS Playwright input-ot
21
+ * használjon (pl. `await page.getByRole('button', ...).click()`), különben
22
+ * az INP üres marad. A flush + olvasás ezután automatikus.
23
+ */
24
+ interact?: (page: Page) => Promise<void>;
25
+ }
26
+ //# sourceMappingURL=fdp-cwv-route-config.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-route-config.interface.js.map
@@ -0,0 +1,27 @@
1
+ import { FDP_CWV_Metric_Interface } from './fdp-cwv-metric.interface';
2
+ /**
3
+ * Egy route CWV mérési eredménye.
4
+ *
5
+ * Bármelyik metrika hiányozhat: pl. az INP csak akkor van, ha volt valós
6
+ * interakció; a CLS 0 lehet, ha nincs shift. Az `overBudget` a route azon
7
+ * metrikáit sorolja fel ('lcp'|'inp'|'cls'), amik a konfigurált budget fölött vannak.
8
+ */
9
+ export interface FDP_CWV_RouteResult_Interface {
10
+ /** A route path-je. */
11
+ route: string;
12
+ /** Olvasható címke (a config `label`-je vagy a path). */
13
+ label: string;
14
+ /** Largest Contentful Paint (ms). */
15
+ lcp?: FDP_CWV_Metric_Interface;
16
+ /** Interaction to Next Paint (ms). */
17
+ inp?: FDP_CWV_Metric_Interface;
18
+ /** Cumulative Layout Shift (dimenzió-nélküli). */
19
+ cls?: FDP_CWV_Metric_Interface;
20
+ /** First Contentful Paint (ms) — diagnosztikai. */
21
+ fcp?: FDP_CWV_Metric_Interface;
22
+ /** Time To First Byte (ms) — diagnosztikai. */
23
+ ttfb?: FDP_CWV_Metric_Interface;
24
+ /** A budget fölötti metrikák kulcsai, pl. ['lcp','cls']. */
25
+ overBudget: string[];
26
+ }
27
+ //# sourceMappingURL=fdp-cwv-route-result.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-route-result.interface.js.map
@@ -0,0 +1,35 @@
1
+ import { FDP_CWV_RouteConfig_Interface } from './fdp-cwv-route-config.interface';
2
+ import { FDP_CWV_Budgets_Interface } from './fdp-cwv-budgets.interface';
3
+ /**
4
+ * A `FDP_CWV_Collector_Util.collect()` bemeneti konfigurációja.
5
+ *
6
+ * A `webVitalsScript` a `web-vitals/attribution` IIFE-bundle FORRÁSA string-ként.
7
+ * A shipped fdp-e2e-helpers SZÁNDÉKOSAN nem importálja a web-vitals-t (és nem
8
+ * használ require-t) — a fogyasztó e2e projekt (ahol a `require.resolve` megengedett)
9
+ * olvassa be a fájlt és adja át:
10
+ *
11
+ * const wv = fs.readFileSync(require.resolve('web-vitals/dist/web-vitals.attribution.iife.js'), 'utf-8');
12
+ *
13
+ * A bundle a böngészőben `window.webVitals`-t definiál (onLCP/onINP/onCLS/onFCP/onTTFB).
14
+ */
15
+ export interface FDP_CWV_RunConfig_Interface {
16
+ /** A mérendő route-ok listája (sorrendben). */
17
+ routes: FDP_CWV_RouteConfig_Interface[];
18
+ /** A `web-vitals/attribution` IIFE-bundle forrása string-ként (lásd fent). */
19
+ webVitalsScript: string;
20
+ /** Opcionális budget-küszöbök; ha hiányzik, nincs budget-check (csak rating). */
21
+ budgets?: FDP_CWV_Budgets_Interface;
22
+ /**
23
+ * FDP komponens-prefixek (pl. ['s-','ad-','m1-','fr-']) az attribution-target
24
+ * komponens-szintű annotálásához. A `generateTarget` a legközelebbi olyan
25
+ * ős-elemet keresi, aminek a tag-prefixe ebben a listában van, és azt teszi a
26
+ * selector elé — így a riport komponens-szinten beszél. Default: csak a natív
27
+ * web-vitals selector.
28
+ */
29
+ componentPrefixes?: string[];
30
+ /** Várakozás a visibility-flush után ms-ben (a callback-eknek idő kell). Default: 600. */
31
+ flushTimeoutMs?: number;
32
+ /** Navigáció timeout ms-ben route-onként. Default: 20000. */
33
+ navigationTimeoutMs?: number;
34
+ }
35
+ //# sourceMappingURL=fdp-cwv-run-config.interface.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fdp-cwv-run-config.interface.js.map
package/build/index.d.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  export * from './_collections/e2e-hard-delete.util';
2
2
  export * from './_collections/e2e-unique-generator.util';
3
+ export * from './_collections/fdp-cwv-collector.util';
3
4
  export * from './_services/e2e-config.service';
4
5
  export * from './_services/e2e-logger.service';
5
6
  export * from './_services/e2e-user-manager.service';
6
7
  export * from './_models/interfaces/e2e-hard-delete-options.interface';
7
8
  export * from './_models/interfaces/e2e-test-config.interface';
8
9
  export * from './_models/interfaces/e2e-user.interface';
10
+ export * from './_models/interfaces/fdp-cwv-run-config.interface';
11
+ export * from './_models/interfaces/fdp-cwv-route-config.interface';
12
+ export * from './_models/interfaces/fdp-cwv-budgets.interface';
13
+ export * from './_models/interfaces/fdp-cwv-result.interface';
14
+ export * from './_models/interfaces/fdp-cwv-route-result.interface';
15
+ export * from './_models/interfaces/fdp-cwv-metric.interface';
16
+ export * from './_models/interfaces/fdp-cwv-attribution.interface';
17
+ export * from './_models/interfaces/fdp-cwv-raw-metric.interface';
9
18
  //# sourceMappingURL=index.d.ts.map
package/build/index.js CHANGED
@@ -4,6 +4,7 @@ const tslib_1 = require("tslib");
4
4
  // COLLECTIONS / UTILS
5
5
  tslib_1.__exportStar(require("./_collections/e2e-hard-delete.util"), exports);
6
6
  tslib_1.__exportStar(require("./_collections/e2e-unique-generator.util"), exports);
7
+ tslib_1.__exportStar(require("./_collections/fdp-cwv-collector.util"), exports);
7
8
  // SERVICES
8
9
  tslib_1.__exportStar(require("./_services/e2e-config.service"), exports);
9
10
  tslib_1.__exportStar(require("./_services/e2e-logger.service"), exports);
@@ -12,4 +13,12 @@ tslib_1.__exportStar(require("./_services/e2e-user-manager.service"), exports);
12
13
  tslib_1.__exportStar(require("./_models/interfaces/e2e-hard-delete-options.interface"), exports);
13
14
  tslib_1.__exportStar(require("./_models/interfaces/e2e-test-config.interface"), exports);
14
15
  tslib_1.__exportStar(require("./_models/interfaces/e2e-user.interface"), exports);
16
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-run-config.interface"), exports);
17
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-route-config.interface"), exports);
18
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-budgets.interface"), exports);
19
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-result.interface"), exports);
20
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-route-result.interface"), exports);
21
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-metric.interface"), exports);
22
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-attribution.interface"), exports);
23
+ tslib_1.__exportStar(require("./_models/interfaces/fdp-cwv-raw-metric.interface"), exports);
15
24
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futdevpro/fdp-e2e-helpers",
3
- "version": "01.15.10",
3
+ "version": "01.15.14",
4
4
  "description": "Shared E2E test helpers for FDP-stack apps. First util: E2E_HardDelete_Util. Stateless utility-collection — NOT a framework.",
5
5
  "DyBu_settings": {
6
6
  "packageType": "shared-package",
@@ -70,8 +70,9 @@
70
70
  "HOWTO.md"
71
71
  ],
72
72
  "peerDependencies": {
73
- "@futdevpro/fsm-dynamo": ">=1.15.0",
74
- "@futdevpro/nts-dynamo": ">=1.15.0"
73
+ "@futdevpro/fsm-dynamo": "1.15.16",
74
+ "@futdevpro/nts-dynamo": "1.15.34",
75
+ "@playwright/test": ">=1.49.0"
75
76
  },
76
77
  "dependencies": {
77
78
  "axios": "~1.8.1",
@@ -80,8 +81,9 @@
80
81
  },
81
82
  "devDependencies": {
82
83
  "@futdevpro/dynamo-eslint": "1.15.10",
83
- "@futdevpro/fsm-dynamo": "1.15.13",
84
- "@futdevpro/nts-dynamo": "1.15.31",
84
+ "@futdevpro/fsm-dynamo": "1.15.16",
85
+ "@futdevpro/nts-dynamo": "1.15.34",
86
+ "@playwright/test": "^1.49.0",
85
87
  "@types/jasmine": "^5.1.5",
86
88
  "@typescript-eslint/eslint-plugin": "^8.41.0",
87
89
  "@typescript-eslint/parser": "^8.41.0",