@arghajit/dummy 0.3.27 → 0.3.28

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,116 @@
1
+ 'use server';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ const reportFileName = 'playwright-pulse-report.json'; // Use a constant for the filename
5
+ const reportFilePath = path.resolve(process.cwd(), reportFileName); // Default path relative to cwd
6
+ let cachedReportData = null;
7
+ let lastReadTime = null;
8
+ const CACHE_DURATION = 5000; // Cache duration in milliseconds (e.g., 5 seconds)
9
+ export async function readReportDataInternal() {
10
+ const now = Date.now();
11
+ // Use cache if it's recent and not forced refresh
12
+ if (cachedReportData && lastReadTime && (now - lastReadTime < CACHE_DURATION)) {
13
+ // console.log("Returning cached report data.");
14
+ return cachedReportData;
15
+ }
16
+ // console.log(`Attempting to read report file from: ${reportFilePath}`);
17
+ try {
18
+ const fileContent = await fs.readFile(reportFilePath, 'utf-8');
19
+ let parsedData;
20
+ try {
21
+ parsedData = JSON.parse(fileContent);
22
+ }
23
+ catch (parseError) {
24
+ console.error(`Error parsing JSON from ${reportFilePath}:`, parseError);
25
+ throw new Error(`Invalid JSON in report file: ${parseError.message}`);
26
+ }
27
+ // --- Date Reviver ---
28
+ // Function to convert ISO date strings back to Date objects recursively
29
+ const reviveDates = (key, value) => {
30
+ // Matches ISO 8601 date format (YYYY-MM-DDTHH:mm:ss.sssZ)
31
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
32
+ if (typeof value === 'string' && isoDateRegex.test(value)) {
33
+ const date = new Date(value);
34
+ // Check if the parsed date is valid before returning
35
+ if (!isNaN(date.getTime())) {
36
+ return date;
37
+ }
38
+ }
39
+ return value;
40
+ };
41
+ // Re-parse with the date reviver
42
+ const reportData = JSON.parse(fileContent, reviveDates);
43
+ // Basic validation after parsing and date revival
44
+ if (!reportData || typeof reportData !== 'object') {
45
+ throw new Error('Report file is empty or not a valid object.');
46
+ }
47
+ if (!reportData.metadata || typeof reportData.metadata.generatedAt !== 'string') { // generatedAt should remain string from JSON
48
+ throw new Error('Invalid or missing metadata in report file.');
49
+ }
50
+ if (!Array.isArray(reportData.results)) {
51
+ throw new Error('Missing or invalid "results" array in report file.');
52
+ }
53
+ // Optional: Validate run data if present
54
+ if (reportData.run && typeof reportData.run !== 'object') {
55
+ throw new Error('Invalid "run" data in report file.');
56
+ }
57
+ if (reportData.run && !(reportData.run.timestamp instanceof Date)) {
58
+ console.warn('Warning: Run timestamp was not correctly revived to a Date object.');
59
+ // Potentially attempt fallback parsing or throw error
60
+ reportData.run.timestamp = new Date(reportData.run.timestamp); // Attempt fallback
61
+ if (isNaN(reportData.run.timestamp.getTime())) {
62
+ throw new Error('Invalid run timestamp format.');
63
+ }
64
+ }
65
+ // Validate dates within results and steps
66
+ reportData.results.forEach((result, index) => {
67
+ if (!(result.startTime instanceof Date) || !(result.endTime instanceof Date)) {
68
+ console.warn(`Warning: Invalid start/end time for result index ${index}. Attempting fallback parsing.`);
69
+ result.startTime = new Date(result.startTime);
70
+ result.endTime = new Date(result.endTime);
71
+ if (isNaN(result.startTime.getTime()) || isNaN(result.endTime.getTime())) {
72
+ throw new Error(`Invalid start/end time in result index ${index}.`);
73
+ }
74
+ }
75
+ if (Array.isArray(result.steps)) {
76
+ result.steps.forEach((step, stepIndex) => {
77
+ if (!(step.startTime instanceof Date) || !(step.endTime instanceof Date)) {
78
+ console.warn(`Warning: Invalid start/end time for step index ${stepIndex} in result index ${index}. Attempting fallback parsing.`);
79
+ step.startTime = new Date(step.startTime);
80
+ step.endTime = new Date(step.endTime);
81
+ if (isNaN(step.startTime.getTime()) || isNaN(step.endTime.getTime())) {
82
+ throw new Error(`Invalid start/end time in step index ${stepIndex}, result index ${index}.`);
83
+ }
84
+ }
85
+ });
86
+ }
87
+ else {
88
+ result.steps = []; // Initialize if steps array is missing
89
+ }
90
+ });
91
+ cachedReportData = reportData;
92
+ lastReadTime = now;
93
+ // console.log("Successfully read, parsed, and cached report file.");
94
+ return reportData;
95
+ }
96
+ catch (error) {
97
+ if (error.code === 'ENOENT') {
98
+ console.warn(`Report file not found at ${reportFilePath}. Returning empty data structure. Ensure Playwright tests ran with the reporter enabled and the file exists.`);
99
+ // Return a valid, empty structure
100
+ const defaultReport = {
101
+ run: null,
102
+ results: [],
103
+ metadata: { generatedAt: new Date().toISOString() }
104
+ };
105
+ cachedReportData = defaultReport; // Cache the default empty state
106
+ lastReadTime = now;
107
+ return defaultReport;
108
+ }
109
+ else {
110
+ // Log the specific error for debugging
111
+ console.error(`Error processing report file at ${reportFilePath}:`, error);
112
+ // Propagate a user-friendly error
113
+ throw new Error(`Failed to load report data: ${error.message}`);
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,39 @@
1
+ 'use server';
2
+ import { readReportDataInternal } from './data-reader'; // Import the server-only reader
3
+ // --- Modified Data Fetching Functions ---
4
+ export const getLatestTestRun = async () => {
5
+ await new Promise(resolve => setTimeout(resolve, 20)); // Minimal delay for demo
6
+ const reportData = await readReportDataInternal();
7
+ return reportData.run;
8
+ };
9
+ export const getTestResultById = async (testId) => {
10
+ await new Promise(resolve => setTimeout(resolve, 20)); // Minimal delay for demo
11
+ const reportData = await readReportDataInternal();
12
+ return reportData.results.find(r => r.id === testId) || null;
13
+ };
14
+ export const getTrendData = async (limit = 1) => {
15
+ await new Promise(resolve => setTimeout(resolve, 30)); // Minimal delay for demo
16
+ const reportData = await readReportDataInternal();
17
+ if (reportData.run) {
18
+ const run = reportData.run;
19
+ // Return a single data point based on the current run
20
+ // You might want a more sophisticated trend mechanism later
21
+ return [{
22
+ date: run.timestamp.toISOString().split('T')[0], // Use date part of timestamp
23
+ passed: run.passed,
24
+ failed: run.failed,
25
+ skipped: run.skipped,
26
+ }];
27
+ }
28
+ return [];
29
+ };
30
+ export const getAllTestResults = async () => {
31
+ await new Promise(resolve => setTimeout(resolve, 50)); // Minimal delay for demo
32
+ const reportData = await readReportDataInternal();
33
+ return reportData.results;
34
+ };
35
+ export const getTestRuns = async (limit = 1) => {
36
+ await new Promise(resolve => setTimeout(resolve, 30)); // Minimal delay for demo
37
+ const reportData = await readReportDataInternal();
38
+ return reportData.run ? [reportData.run] : [];
39
+ };
@@ -0,0 +1 @@
1
+ export function cn(...inputs) { }
package/dist/pulse.js CHANGED
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pulse = void 0;
4
- const test_1 = require("@playwright/test");
5
- exports.pulse = {
1
+ import { test } from "@playwright/test";
2
+ export const pulse = {
6
3
  /**
7
4
  * Sets the severity level for the current test.
8
5
  * * @param level - The severity level ('Minor' | 'Low' | 'Medium' | 'High' | 'Critical')
@@ -16,7 +13,7 @@ exports.pulse = {
16
13
  // Default to "Medium" if an invalid string is passed
17
14
  const selectedLevel = validLevels.includes(level) ? level : "Medium";
18
15
  // Add the annotation to Playwright's test info
19
- test_1.test.info().annotations.push({
16
+ test.info().annotations.push({
20
17
  type: "pulse_severity",
21
18
  description: selectedLevel,
22
19
  });
@@ -368,8 +368,9 @@ class PlaywrightPulseReporter {
368
368
  const lastAttempt = attempts[attempts.length - 1];
369
369
  firstAttempt.final_status = lastAttempt.status;
370
370
  // If the last attempt was flaky, ensure outcome is set on the main result
371
- if (lastAttempt.outcome === 'flaky') {
371
+ if (lastAttempt.outcome === 'flaky' || lastAttempt.status === 'flaky') {
372
372
  firstAttempt.outcome = 'flaky';
373
+ firstAttempt.status = 'flaky';
373
374
  }
374
375
  }
375
376
  else {
@@ -438,6 +439,7 @@ class PlaywrightPulseReporter {
438
439
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
439
440
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
440
441
  finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
442
+ finalRunData.flaky = finalResultsList.filter((r) => r.status === "flaky").length;
441
443
  finalRunData.totalTests = finalResultsList.length;
442
444
  const reviveDates = (key, value) => {
443
445
  const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
@@ -498,6 +500,7 @@ class PlaywrightPulseReporter {
498
500
  passed: finalResults.filter((r) => r.status === "passed").length,
499
501
  failed: finalResults.filter((r) => r.status === "failed").length,
500
502
  skipped: finalResults.filter((r) => r.status === "skipped").length,
503
+ flaky: finalResults.filter((r) => r.status === "flaky").length,
501
504
  duration,
502
505
  environment: environmentDetails,
503
506
  };
@@ -0,0 +1 @@
1
+ {"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.es2021.d.ts","../node_modules/typescript/lib/lib.es2022.d.ts","../node_modules/typescript/lib/lib.es2023.d.ts","../node_modules/typescript/lib/lib.es2024.d.ts","../node_modules/typescript/lib/lib.esnext.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2016.intl.d.ts","../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2017.date.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.es2021.promise.d.ts","../node_modules/typescript/lib/lib.es2021.string.d.ts","../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../node_modules/typescript/lib/lib.es2021.intl.d.ts","../node_modules/typescript/lib/lib.es2022.array.d.ts","../node_modules/typescript/lib/lib.es2022.error.d.ts","../node_modules/typescript/lib/lib.es2022.intl.d.ts","../node_modules/typescript/lib/lib.es2022.object.d.ts","../node_modules/typescript/lib/lib.es2022.string.d.ts","../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../node_modules/typescript/lib/lib.es2023.array.d.ts","../node_modules/typescript/lib/lib.es2023.collection.d.ts","../node_modules/typescript/lib/lib.es2023.intl.d.ts","../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2024.collection.d.ts","../node_modules/typescript/lib/lib.es2024.object.d.ts","../node_modules/typescript/lib/lib.es2024.promise.d.ts","../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2024.string.d.ts","../node_modules/typescript/lib/lib.esnext.array.d.ts","../node_modules/typescript/lib/lib.esnext.collection.d.ts","../node_modules/typescript/lib/lib.esnext.intl.d.ts","../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/playwright-core/types/protocol.d.ts","../node_modules/playwright-core/types/structs.d.ts","../node_modules/playwright-core/types/types.d.ts","../node_modules/playwright/types/test.d.ts","../node_modules/playwright/test.d.ts","../node_modules/@playwright/test/index.d.ts","../src/pulse.ts","../node_modules/lucide-react/dist/lucide-react.d.ts","../src/types/index.ts","../src/lib/report-types.ts","../src/lib/data-reader.ts","../src/lib/data.ts","../node_modules/clsx/clsx.d.ts","../src/lib/utils.ts","../node_modules/playwright/types/testreporter.d.ts","../node_modules/@playwright/test/reporter.d.ts","../src/reporter/attachment-utils.ts","../node_modules/@types/ua-parser-js/index.d.ts","../src/reporter/playwright-pulse-reporter.ts","../src/reporter/index.ts","../node_modules/@types/estree/index.d.ts","../node_modules/@types/json-schema/index.d.ts","../node_modules/@types/node/compatibility/disposable.d.ts","../node_modules/@types/node/compatibility/indexable.d.ts","../node_modules/@types/node/compatibility/iterators.d.ts","../node_modules/@types/node/compatibility/index.d.ts","../node_modules/@types/node/globals.typedarray.d.ts","../node_modules/@types/node/buffer.buffer.d.ts","../node_modules/undici-types/header.d.ts","../node_modules/undici-types/readable.d.ts","../node_modules/undici-types/file.d.ts","../node_modules/undici-types/fetch.d.ts","../node_modules/undici-types/formdata.d.ts","../node_modules/undici-types/connector.d.ts","../node_modules/undici-types/client.d.ts","../node_modules/undici-types/errors.d.ts","../node_modules/undici-types/dispatcher.d.ts","../node_modules/undici-types/global-dispatcher.d.ts","../node_modules/undici-types/global-origin.d.ts","../node_modules/undici-types/pool-stats.d.ts","../node_modules/undici-types/pool.d.ts","../node_modules/undici-types/handlers.d.ts","../node_modules/undici-types/balanced-pool.d.ts","../node_modules/undici-types/agent.d.ts","../node_modules/undici-types/mock-interceptor.d.ts","../node_modules/undici-types/mock-agent.d.ts","../node_modules/undici-types/mock-client.d.ts","../node_modules/undici-types/mock-pool.d.ts","../node_modules/undici-types/mock-errors.d.ts","../node_modules/undici-types/proxy-agent.d.ts","../node_modules/undici-types/env-http-proxy-agent.d.ts","../node_modules/undici-types/retry-handler.d.ts","../node_modules/undici-types/retry-agent.d.ts","../node_modules/undici-types/api.d.ts","../node_modules/undici-types/interceptors.d.ts","../node_modules/undici-types/util.d.ts","../node_modules/undici-types/cookies.d.ts","../node_modules/undici-types/patch.d.ts","../node_modules/undici-types/websocket.d.ts","../node_modules/undici-types/eventsource.d.ts","../node_modules/undici-types/filereader.d.ts","../node_modules/undici-types/diagnostics-channel.d.ts","../node_modules/undici-types/content-type.d.ts","../node_modules/undici-types/cache.d.ts","../node_modules/undici-types/index.d.ts","../node_modules/@types/node/globals.d.ts","../node_modules/@types/node/assert.d.ts","../node_modules/@types/node/assert/strict.d.ts","../node_modules/@types/node/async_hooks.d.ts","../node_modules/@types/node/buffer.d.ts","../node_modules/@types/node/child_process.d.ts","../node_modules/@types/node/cluster.d.ts","../node_modules/@types/node/console.d.ts","../node_modules/@types/node/constants.d.ts","../node_modules/@types/node/crypto.d.ts","../node_modules/@types/node/dgram.d.ts","../node_modules/@types/node/diagnostics_channel.d.ts","../node_modules/@types/node/dns.d.ts","../node_modules/@types/node/dns/promises.d.ts","../node_modules/@types/node/domain.d.ts","../node_modules/@types/node/dom-events.d.ts","../node_modules/@types/node/events.d.ts","../node_modules/@types/node/fs.d.ts","../node_modules/@types/node/fs/promises.d.ts","../node_modules/@types/node/http.d.ts","../node_modules/@types/node/http2.d.ts","../node_modules/@types/node/https.d.ts","../node_modules/@types/node/inspector.d.ts","../node_modules/@types/node/module.d.ts","../node_modules/@types/node/net.d.ts","../node_modules/@types/node/os.d.ts","../node_modules/@types/node/path.d.ts","../node_modules/@types/node/perf_hooks.d.ts","../node_modules/@types/node/process.d.ts","../node_modules/@types/node/punycode.d.ts","../node_modules/@types/node/querystring.d.ts","../node_modules/@types/node/readline.d.ts","../node_modules/@types/node/readline/promises.d.ts","../node_modules/@types/node/repl.d.ts","../node_modules/@types/node/sea.d.ts","../node_modules/@types/node/stream.d.ts","../node_modules/@types/node/stream/promises.d.ts","../node_modules/@types/node/stream/consumers.d.ts","../node_modules/@types/node/stream/web.d.ts","../node_modules/@types/node/string_decoder.d.ts","../node_modules/@types/node/test.d.ts","../node_modules/@types/node/timers.d.ts","../node_modules/@types/node/timers/promises.d.ts","../node_modules/@types/node/tls.d.ts","../node_modules/@types/node/trace_events.d.ts","../node_modules/@types/node/tty.d.ts","../node_modules/@types/node/url.d.ts","../node_modules/@types/node/util.d.ts","../node_modules/@types/node/v8.d.ts","../node_modules/@types/node/vm.d.ts","../node_modules/@types/node/wasi.d.ts","../node_modules/@types/node/worker_threads.d.ts","../node_modules/@types/node/zlib.d.ts","../node_modules/@types/node/index.d.ts"],"fileIdsList":[[83,106,148],[93,106,148],[106,148],[106,145,148],[106,147,148],[148],[106,148,153,182],[106,148,149,154,160,161,168,179,190],[106,148,149,150,160,168],[101,102,103,106,148],[106,148,151,191],[106,148,152,153,161,169],[106,148,153,179,187],[106,148,154,156,160,168],[106,147,148,155],[106,148,156,157],[106,148,160],[106,148,158,160],[106,147,148,160],[106,148,160,161,162,179,190],[106,148,160,161,162,175,179,182],[106,143,148,195],[106,148,156,160,163,168,179,190],[106,148,160,161,163,164,168,179,187,190],[106,148,163,165,179,187,190],[104,105,106,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196],[106,148,160,166],[106,148,167,190,195],[106,148,156,160,168,179],[106,148,169],[106,148,170],[106,147,148,171],[106,145,146,147,148,149,150,151,152,153,154,155,156,157,158,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196],[106,148,173],[106,148,174],[106,148,160,175,176],[106,148,175,177,191,193],[106,148,160,179,180,181,182],[106,148,179,181],[106,148,179,180],[106,148,182],[106,148,183],[106,145,148,179],[106,148,160,185,186],[106,148,185,186],[106,148,153,168,179,187],[106,148,188],[106,148,168,189],[106,148,163,174,190],[106,148,153,191],[106,148,179,192],[106,148,167,193],[106,148,194],[106,148,153,160,162,171,179,190,193,195],[106,148,179,196],[81,106,148],[79,80,106,148,149,161,179],[82,106,148],[106,115,119,148,190],[106,115,148,179,190],[106,110,148],[106,112,115,148,187,190],[106,148,168,187],[106,148,197],[106,110,148,197],[106,112,115,148,168,190],[106,107,108,111,114,148,160,179,190],[106,115,122,148],[106,107,113,148],[106,115,136,137,148],[106,111,115,148,182,190,197],[106,136,148,197],[106,109,110,148,197],[106,115,148],[106,109,110,111,112,113,114,115,116,117,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,137,138,139,140,141,142,148],[106,115,130,148],[106,115,122,123,148],[106,113,115,123,124,148],[106,114,148],[106,107,110,115,148],[106,115,119,123,124,148],[106,119,148],[106,113,115,118,148,190],[106,107,112,115,122,148],[106,148,179],[106,110,115,136,148,195,197],[88,106,148,162,170],[87,89,106,148],[87,106,148],[91,106,148],[84,106,148],[87,94,106,148,161,170],[85,87,88,97,106,148],[87,88,94,96,106,148,153,162,169,170],[86,106,148]],"fileInfos":[{"version":"e41c290ef7dd7dab3493e6cbe5909e0148edf4a8dad0271be08edec368a0f7b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"e12a46ce14b817d4c9e6b2b478956452330bf00c9801b79de46f7a1815b5bd40","impliedFormat":1},{"version":"4fd3f3422b2d2a3dfd5cdd0f387b3a8ec45f006c6ea896a4cb41264c2100bb2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"69e65d976bf166ce4a9e6f6c18f94d2424bf116e90837ace179610dbccad9b42","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"62bb211266ee48b2d0edf0d8d1b191f0c24fc379a82bd4c1692a082c540bc6b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"936e80ad36a2ee83fc3caf008e7c4c5afe45b3cf3d5c24408f039c1d47bdc1df","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fef8cfad2e2dc5f5b3d97a6f4f2e92848eb1b88e897bb7318cef0e2820bceaab","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f1e2a172204962276504466a6393426d2ca9c54894b1ad0a6c9dad867a65f876","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"b5ce7a470bc3628408429040c4e3a53a27755022a32fd05e2cb694e7015386c7","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"bab26767638ab3557de12c900f0b91f710c7dc40ee9793d5a27d32c04f0bf646","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"61d6a2092f48af66dbfb220e31eea8b10bc02b6932d6e529005fd2d7b3281290","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f6ef9c13ca4e92bc06426a01f2c3198448e949f64f62eb02e2db23146a8d06c","impliedFormat":1},{"version":"32727845ab5bd8a9ef3e4844c567c09f6d418fcf0f90d381c00652a6f23e7f6e","impliedFormat":1},{"version":"254bdfc7c096df05586db7e69fd2c6d78c766d13713fb5a44b74d49fd45e8881","impliedFormat":1},{"version":"4a405da2b4fe59c59b09a9af656f7b40c18293b1e17118a93580585c01c7dc7a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f00324f263189b385c3a9383b1f4dae6237697bcf0801f96aa35c340512d79c","impliedFormat":1},{"version":"ec8997c2e5cea26befc76e7bf990750e96babb16977673a9ff3b5c0575d01e48","impliedFormat":1},"88a8818ff468a6bfbd1e798cf10688016e1035b81b841162722a956be0e3d417",{"version":"6717dad91e44ad22d68f1fc0db74e5eb5398c2c06a2943bf06d3a168e8b1ba45","impliedFormat":99},"e671563fbeb355f1e10a8296d3ff5ab2c1bf362801dbab2548e15e826adc3a71","331a971aff7a6da8693ab1ab38961996fecd854f6a639d430c81f8207dc92e96","b45a158fb0a2eae42e6c5d88d674ec5c72c610cc4840c8e9bb7178445dc4d4e3","83de864087649487b699d01917d4245750b114798d180426480e8019bc8497f5",{"version":"ef73bcfef9907c8b772a30e5a64a6bd86a5669cba3d210fcdcc6b625e3312459","impliedFormat":1},"91087920d3b2235d60b478f3d317c8737b64ce7fadba4dc3f88c9a0fb46080ba",{"version":"c76f94b31ae5c283f441916a9feb84257dca7041625eb7f3c9459fac5ac351cc","impliedFormat":1},{"version":"04392f8e190f9e51301f73d17bbb34babd54858c1efc932d2193962f66dabae2","impliedFormat":1},"c769507a3aab1d5491d133bcece45dd63aed97868e14673f73c11ba15ec90168",{"version":"98cbca6c3c5b2f8d1469dcee88c13304b6cb149fb057ec3b8a85f2e39ff1fc84","impliedFormat":1},"29a00357fe4b27487fa0631dfe3d4908249ed032cc4947b99f4098a7a30ad367","d502b970e98c03636f85ec36014de7c97aa31af702956549e1edc02b12067b03",{"version":"e2b48abff5a8adc6bb1cd13a702b9ef05e6045a98e7cfa95a8779b53b6d0e69d","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"a79e62f1e20467e11a904399b8b18b18c0c6eea6b50c1168bf215356d5bebfaf","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fd06258805d26c72f5997e07a23155d322d5f05387adb3744a791fe6a0b042d","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"24bd580b5743dc56402c440dc7f9a4f5d592ad7a419f25414d37a7bfe11e342b","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"6bdc71028db658243775263e93a7db2fd2abfce3ca569c3cca5aee6ed5eb186d","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"4d2b0eb911816f66abe4970898f97a2cfc902bcd743cbfa5017fad79f7ef90d8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"e53a3c2a9f624d90f24bf4588aacd223e7bec1b9d0d479b68d2f4a9e6011147f","impliedFormat":1},{"version":"24b8685c62562f5d98615c5a0c1d05f297cf5065f15246edfe99e81ec4c0e011","impliedFormat":1},{"version":"93507c745e8f29090efb99399c3f77bec07db17acd75634249dc92f961573387","impliedFormat":1},{"version":"339dc5265ee5ed92e536a93a04c4ebbc2128f45eeec6ed29f379e0085283542c","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"08faa97886e71757779428dd4c69a545c32c85fd629d1116d42710b32c6378bc","affectsGlobalScope":true,"impliedFormat":1},{"version":"a72ffc815104fb5c075106ebca459b2d55d07862a773768fce89efc621b3964b","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"3d77c73be94570813f8cadd1f05ebc3dc5e2e4fdefe4d340ca20cd018724ee36","impliedFormat":1},{"version":"d674383111e06b6741c4ad2db962131b5b0fa4d0294b998566c635e86195a453","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3e58c4c18a031cbb17abec7a4ad0bd5ae9fc70c1f4ba1e7fb921ad87c504aca","impliedFormat":1},{"version":"a3e8bafb2af8e850c644f4be7f5156cf7d23b7bfdc3b786bd4d10ed40329649c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","impliedFormat":1},{"version":"f77d9188e41291acf14f476e931972460a303e1952538f9546e7b370cb8d0d20","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d04e3640dd9eb67f7f1e5bd3d0bf96c784666f7aefc8ac1537af6f2d38d4c29","impliedFormat":1},{"version":"3c884d9d9ec454bdf0d5a0b8465bf8297d2caa4d853851d92cc417ac6f30b969","impliedFormat":1},{"version":"5a369483ac4cfbdf0331c248deeb36140e6907db5e1daed241546b4a2055f82c","impliedFormat":1},{"version":"e8f5b5cc36615c17d330eaf8eebbc0d6bdd942c25991f96ef122f246f4ff722f","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ada07543808f3b967624645a8e1ccd446f8b01ade47842acf1328aec899fed0","affectsGlobalScope":true,"impliedFormat":1},{"version":"c4a806152acbef81593f96cae6f2b04784d776457d97adbe2694478b243fcf03","impliedFormat":1},{"version":"71adf5dbc59568663d252a46179e71e4d544c053978bfc526d11543a3f716f42","impliedFormat":1},{"version":"c60db41f7bee80fb80c0b12819f5e465c8c8b465578da43e36d04f4a4646f57d","impliedFormat":1},{"version":"93bd413918fa921c8729cef45302b24d8b6c7855d72d5bf82d3972595ae8dcbf","impliedFormat":1},{"version":"4ff41188773cbf465807dd2f7059c7494cbee5115608efc297383832a1150c43","impliedFormat":1},{"version":"dccdf1677e531e33f8ac961a68bc537418c9a414797c1ea7e91307501cdc3f5e","impliedFormat":1},{"version":"e184c4b8918ef56c8c9e68bd79f3f3780e2d0d75bf2b8a41da1509a40c2deb46","affectsGlobalScope":true,"impliedFormat":1},{"version":"d206b4baf4ddcc15d9d69a9a2f4999a72a2c6adeaa8af20fa7a9960816287555","impliedFormat":1},{"version":"93f437e1398a4f06a984f441f7fa7a9f0535c04399619b5c22e0b87bdee182cb","impliedFormat":1},{"version":"afbe24ab0d74694372baa632ecb28bb375be53f3be53f9b07ecd7fc994907de5","impliedFormat":1},{"version":"70731d10d5311bd4cf710ef7f6539b62660f4b0bfdbb3f9fbe1d25fe6366a7fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"6b19db3600a17af69d4f33d08cc7076a7d19fb65bb36e442cac58929ec7c9482","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"137c2894e8f3e9672d401cc0a305dc7b1db7c69511cf6d3970fb53302f9eae09","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"8145e07aad6da5f23f2fcd8c8e4c5c13fb26ee986a79d03b0829b8fce152d8b2","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"235bfb54b4869c26f7e98e3d1f68dbfc85acf4cf5c38a4444a006fbf74a8a43d","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"93452d394fdd1dc551ec62f5042366f011a00d342d36d50793b3529bfc9bd633","impliedFormat":1},{"version":"bb715efb4857eb94539eafb420352105a0cff40746837c5140bf6b035dd220ba","affectsGlobalScope":true,"impliedFormat":1},{"version":"1851a3b4db78664f83901bb9cac9e45e03a37bb5933cc5bf37e10bb7e91ab4eb","impliedFormat":1},{"version":"fdedf82878e4c744bc2a1c1e802ae407d63474da51f14a54babe039018e53d8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"27d8987fd22d92efe6560cf0ce11767bf089903ffe26047727debfd1f3bf438b","affectsGlobalScope":true,"impliedFormat":1},{"version":"578d8bb6dcb2a1c03c4c3f8eb71abc9677e1a5c788b7f24848e3138ce17f3400","impliedFormat":1},{"version":"4f029899f9bae07e225c43aef893590541b2b43267383bf5e32e3a884d219ed5","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"5b566927cad2ed2139655d55d690ffa87df378b956e7fe1c96024c4d9f75c4cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"bce947017cb7a2deebcc4f5ba04cead891ce6ad1602a4438ae45ed9aa1f39104","affectsGlobalScope":true,"impliedFormat":1},{"version":"d3dffd70e6375b872f0b4e152de4ae682d762c61a24881ecc5eb9f04c5caf76f","impliedFormat":1},{"version":"e2c72c065a36bc9ab2a00ac6a6f51e71501619a72c0609defd304d46610487a4","impliedFormat":1},{"version":"d91a7d8b5655c42986f1bdfe2105c4408f472831c8f20cf11a8c3345b6b56c8c","impliedFormat":1},{"version":"616075a6ac578cf5a013ee12964188b4412823796ce0b202c6f1d2e4ca8480d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8a979b8af001c9fc2e774e7809d233c8ca955a28756f52ee5dee88ccb0611d2","impliedFormat":1},{"version":"cac793cc47c29e26e4ac3601dcb00b4435ebed26203485790e44f2ad8b6ad847","impliedFormat":1}],"root":[85,[87,90],92,95,97,98],"options":{"allowJs":true,"esModuleInterop":true,"jsx":4,"module":99,"outDir":"./","skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[84,1],[94,2],[99,3],[100,3],[145,4],[146,4],[147,5],[106,6],[148,7],[149,8],[150,9],[101,3],[104,10],[102,3],[103,3],[151,11],[152,12],[153,13],[154,14],[155,15],[156,16],[157,16],[159,17],[158,18],[160,19],[161,20],[162,21],[144,22],[105,3],[163,23],[164,24],[165,25],[197,26],[166,27],[167,28],[168,29],[169,30],[170,31],[171,32],[172,33],[173,34],[174,35],[175,36],[176,36],[177,37],[178,3],[179,38],[181,39],[180,40],[182,41],[183,42],[184,43],[185,44],[186,45],[187,46],[188,47],[189,48],[190,49],[191,50],[192,51],[193,52],[194,53],[195,54],[196,55],[96,3],[91,3],[86,3],[79,3],[80,56],[81,57],[83,58],[82,56],[93,58],[77,3],[78,3],[13,3],[14,3],[16,3],[15,3],[2,3],[17,3],[18,3],[19,3],[20,3],[21,3],[22,3],[23,3],[24,3],[3,3],[25,3],[26,3],[4,3],[27,3],[31,3],[28,3],[29,3],[30,3],[32,3],[33,3],[34,3],[5,3],[35,3],[36,3],[37,3],[38,3],[6,3],[42,3],[39,3],[40,3],[41,3],[43,3],[7,3],[44,3],[49,3],[50,3],[45,3],[46,3],[47,3],[48,3],[8,3],[54,3],[51,3],[52,3],[53,3],[55,3],[9,3],[56,3],[57,3],[58,3],[60,3],[59,3],[61,3],[62,3],[10,3],[63,3],[64,3],[65,3],[11,3],[66,3],[67,3],[68,3],[69,3],[70,3],[1,3],[71,3],[72,3],[12,3],[75,3],[74,3],[73,3],[76,3],[122,59],[132,60],[121,59],[142,61],[113,62],[112,63],[141,64],[135,65],[140,66],[115,67],[129,68],[114,69],[138,70],[110,71],[109,64],[139,72],[111,73],[116,74],[117,3],[120,74],[107,3],[143,75],[133,76],[124,77],[125,78],[127,79],[123,80],[126,81],[136,64],[118,82],[119,83],[128,84],[108,85],[131,76],[130,74],[134,3],[137,86],[89,87],[90,88],[88,89],[92,90],[85,91],[95,92],[98,93],[97,94],[87,95]],"version":"5.7.3"}
@@ -70,6 +70,7 @@ export interface TestRun {
70
70
  passed: number;
71
71
  failed: number;
72
72
  skipped: number;
73
+ flaky?: number;
73
74
  duration: number;
74
75
  environment?: EnvDetails | EnvDetails[];
75
76
  }
@@ -78,6 +79,7 @@ export interface TrendDataPoint {
78
79
  passed: number;
79
80
  failed: number;
80
81
  skipped: number;
82
+ flaky?: number;
81
83
  }
82
84
  export interface SummaryMetric {
83
85
  label: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
3
  "author": "Arghajit Singha",
4
- "version": "0.3.27",
4
+ "version": "0.3.28",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "homepage": "https://arghajit47.github.io/playwright-pulse/",
7
7
  "repository": {
@@ -295,6 +295,12 @@ function generateTestTrendsChart(trendData) {
295
295
  color: "var(--warning-color)",
296
296
  marker: { symbol: "circle" },
297
297
  },
298
+ {
299
+ name: "Flaky",
300
+ data: runs.map((r) => r.flaky || 0),
301
+ color: "var(--neutral-500)",
302
+ marker: { symbol: "circle" },
303
+ },
298
304
  ];
299
305
  const runsForTooltip = runs.map((r) => ({
300
306
  runId: r.runId,
@@ -481,6 +487,9 @@ function generateTestHistoryChart(history) {
481
487
  case "skipped":
482
488
  color = "var(--warning-color)";
483
489
  break;
490
+ case "flaky":
491
+ color = "var(--neutral-500)";
492
+ break;
484
493
  default:
485
494
  color = "var(--dark-gray-color)";
486
495
  }
@@ -590,6 +599,9 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
590
599
  case "Failed":
591
600
  color = "var(--danger-color)";
592
601
  break;
602
+ case "Flaky":
603
+ color = "var(--neutral-500)";
604
+ break;
593
605
  case "Skipped":
594
606
  color = "var(--warning-color)";
595
607
  break;
@@ -828,7 +840,9 @@ function generateEnvironmentSection(environmentData) {
828
840
  }
829
841
 
830
842
  function generateEnvironmentDashboard(environment, hideHeader = false) {
831
- const cpuInfo = `model: ${environment.cpu.model}, cores: ${environment.cpu.cores}`;
843
+ const cpuModel = environment.cpu && environment.cpu.model ? environment.cpu.model : "N/A";
844
+ const cpuCores = environment.cpu && environment.cpu.cores ? environment.cpu.cores : "N/A";
845
+ const cpuInfo = `model: ${cpuModel}, cores: ${cpuCores}`;
832
846
  const osInfo = environment.os || "N/A";
833
847
  const nodeInfo = environment.node || "N/A";
834
848
  const v8Info = environment.v8 || "N/A";
@@ -1140,7 +1154,7 @@ function generateEnvironmentDashboard(environment, hideHeader = false) {
1140
1154
  </div>
1141
1155
  <div class="env-item-content">
1142
1156
  <p class="env-item-label">Working Dir</p>
1143
- <div class="env-item-value" title="${environment.cwd}">${environment.cwd.length > 30 ? "..." + environment.cwd.slice(-27) : environment.cwd}</div>
1157
+ <div class="env-item-value" title="${cwdInfo}">${cwdInfo.length > 30 ? "..." + cwdInfo.slice(-27) : cwdInfo}</div>
1144
1158
  </div>
1145
1159
  </div>
1146
1160
  </div>
@@ -1164,11 +1178,11 @@ function generateWorkerDistributionChart(results) {
1164
1178
  const workerId =
1165
1179
  typeof test.workerId !== "undefined" ? test.workerId : "N/A";
1166
1180
  if (!acc[workerId]) {
1167
- acc[workerId] = { passed: 0, failed: 0, skipped: 0, tests: [] };
1181
+ acc[workerId] = { passed: 0, failed: 0, skipped: 0, flaky: 0, tests: [] };
1168
1182
  }
1169
1183
 
1170
1184
  const status = String(test.status).toLowerCase();
1171
- if (status === "passed" || status === "failed" || status === "skipped") {
1185
+ if (status === "passed" || status === "failed" || status === "skipped" || status === "flaky") {
1172
1186
  acc[workerId][status]++;
1173
1187
  }
1174
1188
 
@@ -1213,12 +1227,14 @@ function generateWorkerDistributionChart(results) {
1213
1227
  const passedData = workerIds.map((id) => workerData[id].passed);
1214
1228
  const failedData = workerIds.map((id) => workerData[id].failed);
1215
1229
  const skippedData = workerIds.map((id) => workerData[id].skipped);
1230
+ const flakyData = workerIds.map((id) => workerData[id].flaky);
1216
1231
 
1217
1232
  const categoriesString = JSON.stringify(categories);
1218
1233
  const fullDataString = JSON.stringify(fullWorkerData);
1219
1234
  const seriesString = JSON.stringify([
1220
1235
  { name: "Passed", data: passedData, color: "var(--success-color)" },
1221
1236
  { name: "Failed", data: failedData, color: "var(--danger-color)" },
1237
+ { name: "Flaky", data: flakyData, color: "var(--neutral-500)" },
1222
1238
  { name: "Skipped", data: skippedData, color: "var(--warning-color)" },
1223
1239
  ]);
1224
1240
 
@@ -1311,6 +1327,7 @@ function generateWorkerDistributionChart(results) {
1311
1327
  if (test.status === 'passed') color = 'var(--success-color)';
1312
1328
  else if (test.status === 'failed') color = 'var(--danger-color)';
1313
1329
  else if (test.status === 'skipped') color = 'var(--warning-color)';
1330
+ else if (test.status === 'flaky') color = 'var(--neutral-500)';
1314
1331
 
1315
1332
  const escapedName = test.name.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
1316
1333
  testListHtml += \`<li style="color: \${color};"><span style="color: \${color}">[\${test.status.toUpperCase()}]</span> \${escapedName}</li>\`;
@@ -1476,6 +1493,7 @@ function generateTestHistoryContent(trendData) {
1476
1493
  <option value="">All Statuses</option>
1477
1494
  <option value="passed">Passed</option>
1478
1495
  <option value="failed">Failed</option>
1496
+ <option value="flaky">Flaky</option>
1479
1497
  <option value="skipped">Skipped</option>
1480
1498
  </select>
1481
1499
  <button id="clear-history-filters" class="clear-filters-btn">Clear Filters</button>
@@ -1594,6 +1612,7 @@ function getSuitesData(results) {
1594
1612
  browser: browser,
1595
1613
  passed: 0,
1596
1614
  failed: 0,
1615
+ flaky: 0,
1597
1616
  skipped: 0,
1598
1617
  count: 0,
1599
1618
  statusOverall: "passed",
@@ -1601,12 +1620,15 @@ function getSuitesData(results) {
1601
1620
  }
1602
1621
  const suite = suitesMap.get(key);
1603
1622
  suite.count++;
1604
- const currentStatus = String(test.status).toLowerCase();
1623
+ let currentStatus = String(test.status).toLowerCase();
1624
+ if (test.outcome === 'flaky') currentStatus = 'flaky';
1605
1625
  if (currentStatus && suite[currentStatus] !== undefined) {
1606
1626
  suite[currentStatus]++;
1607
1627
  }
1608
1628
  if (currentStatus === "failed") suite.statusOverall = "failed";
1609
- else if (currentStatus === "skipped" && suite.statusOverall !== "failed")
1629
+ else if (currentStatus === "flaky" && suite.statusOverall !== "failed")
1630
+ suite.statusOverall = "flaky";
1631
+ else if (currentStatus === "skipped" && suite.statusOverall !== "failed" && suite.statusOverall !== "flaky")
1610
1632
  suite.statusOverall = "skipped";
1611
1633
  });
1612
1634
  return Array.from(suitesMap.values());
@@ -1656,6 +1678,10 @@ function generateSuitesWidget(suitesData) {
1656
1678
  <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg>
1657
1679
  ${suite.failed}
1658
1680
  </span>
1681
+ <span class="stat-pill flaky" title="Flaky">
1682
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>
1683
+ ${suite.flaky || 0}
1684
+ </span>
1659
1685
  <span class="stat-pill skipped" title="Skipped">
1660
1686
  <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>
1661
1687
  ${suite.skipped}
@@ -2060,6 +2086,7 @@ function generateSeverityDistributionChart(results) {
2060
2086
  const data = {
2061
2087
  passed: [0, 0, 0, 0, 0],
2062
2088
  failed: [0, 0, 0, 0, 0],
2089
+ flaky: [0, 0, 0, 0, 0],
2063
2090
  skipped: [0, 0, 0, 0, 0],
2064
2091
  };
2065
2092
 
@@ -2078,6 +2105,8 @@ function generateSeverityDistributionChart(results) {
2078
2105
  status === "interrupted"
2079
2106
  ) {
2080
2107
  data.failed[index]++;
2108
+ } else if (status === "flaky") {
2109
+ data.flaky[index]++;
2081
2110
  } else {
2082
2111
  data.skipped[index]++;
2083
2112
  }
@@ -2091,6 +2120,7 @@ function generateSeverityDistributionChart(results) {
2091
2120
  const seriesData = [
2092
2121
  { name: "Passed", data: data.passed, color: "var(--success-color)" },
2093
2122
  { name: "Failed", data: data.failed, color: "var(--danger-color)" },
2123
+ { name: "Flaky", data: data.flaky, color: "var(--neutral-500)" },
2094
2124
  { name: "Skipped", data: data.skipped, color: "var(--warning-color)" },
2095
2125
  ];
2096
2126
 
@@ -2204,17 +2234,14 @@ function generateHTML(reportData, trendData = null) {
2204
2234
  return p.replace(new RegExp(`^${DEFAULT_OUTPUT_DIR}[\\\\/]`), "");
2205
2235
  };
2206
2236
 
2207
- const totalTestsOr1 = runSummary.totalTests || 1;
2208
- const passPercentage = Math.round((runSummary.passed / totalTestsOr1) * 100);
2209
- const failPercentage = Math.round((runSummary.failed / totalTestsOr1) * 100);
2210
- const skipPercentage = Math.round(
2211
- ((runSummary.skipped || 0) / totalTestsOr1) * 100,
2212
- );
2237
+
2213
2238
  const avgTestDuration =
2214
2239
  runSummary.totalTests > 0
2215
2240
  ? formatDuration(runSummary.duration / runSummary.totalTests)
2216
2241
  : "0.0s";
2217
2242
 
2243
+ const flakyCount = (results || []).filter(r => r.outcome === 'flaky').length;
2244
+
2218
2245
  // Calculate retry statistics
2219
2246
  const totalRetried = (results || []).reduce((acc, test) => {
2220
2247
  if (test.retryHistory && test.retryHistory.length > 0) {
@@ -2227,19 +2254,30 @@ function generateHTML(reportData, trendData = null) {
2227
2254
  let calculatedPassed = 0;
2228
2255
  let calculatedFailed = 0;
2229
2256
  let calculatedSkipped = 0;
2257
+ let calculatedFlaky = 0;
2230
2258
  let calculatedTotal = 0;
2231
2259
 
2232
2260
  (results || []).forEach(test => {
2233
2261
  calculatedTotal++;
2234
- // If retries exist and final_status is present, use it. Otherwise use test.status.
2235
- const statusToUse = (test.retryHistory && test.retryHistory.length > 0 && test.final_status)
2236
- ? test.final_status
2237
- : test.status;
2262
+ // New Logic: If outcome is 'flaky', it overrides everything.
2263
+ let statusToUse = test.status;
2264
+ if (test.outcome === 'flaky') {
2265
+ statusToUse = 'flaky';
2266
+ } else if (test.status === 'flaky') {
2267
+ // Just in case outcome wasn't set but status was (unlikely with new reporter)
2268
+ statusToUse = 'flaky';
2269
+ } else if (test.retryHistory && test.retryHistory.length > 0 && test.final_status) {
2270
+ statusToUse = test.final_status;
2271
+ }
2272
+
2273
+ // Update test status in place for charts
2274
+ test.status = statusToUse;
2238
2275
 
2239
2276
  const s = String(statusToUse).toLowerCase();
2240
2277
  if (s === 'passed') calculatedPassed++;
2241
2278
  else if (s === 'skipped') calculatedSkipped++;
2242
- else calculatedFailed++; // Treat everything else as failed for simplicity, or add specific checks
2279
+ else if (s === 'flaky') calculatedFlaky++;
2280
+ else calculatedFailed++; // failed, timedout, interrupted
2243
2281
  });
2244
2282
 
2245
2283
  // Override runSummary counts with our calculated ones if results exist
@@ -2247,9 +2285,18 @@ function generateHTML(reportData, trendData = null) {
2247
2285
  runSummary.passed = calculatedPassed;
2248
2286
  runSummary.failed = calculatedFailed;
2249
2287
  runSummary.skipped = calculatedSkipped;
2288
+ runSummary.flaky = calculatedFlaky;
2250
2289
  runSummary.totalTests = calculatedTotal;
2251
2290
  }
2252
2291
 
2292
+ const totalTestsOr1 = runSummary.totalTests || 1;
2293
+ const passPercentage = Math.round((runSummary.passed / totalTestsOr1) * 100);
2294
+ const failPercentage = Math.round((runSummary.failed / totalTestsOr1) * 100);
2295
+ const skipPercentage = Math.round(
2296
+ ((runSummary.skipped || 0) / totalTestsOr1) * 100,
2297
+ );
2298
+ const flakyPercentage = Math.round(((runSummary.flaky || 0) / totalTestsOr1) * 100);
2299
+
2253
2300
 
2254
2301
  // Calculate browser distribution
2255
2302
  const browserStats = (results || []).reduce((acc, test) => {
@@ -2654,7 +2701,7 @@ function generateHTML(reportData, trendData = null) {
2654
2701
  ? test.final_status
2655
2702
  : test.status;
2656
2703
 
2657
- const outcomeBadge = (test.outcome)
2704
+ const outcomeBadge = (test.outcome && test.outcome !== 'flaky')
2658
2705
  ? `<span class="outcome-badge ${test.outcome}">${test.outcome}</span>`
2659
2706
  : '';
2660
2707
 
@@ -2970,12 +3017,17 @@ function generateHTML(reportData, trendData = null) {
2970
3017
  }
2971
3018
  }
2972
3019
 
3020
+
3021
+ .stat-pill.flaky { color: #4b5563; }
3022
+
2973
3023
  .dashboard-grid {
2974
3024
  display: grid;
2975
3025
  grid-template-columns: repeat(4, 1fr);
2976
3026
  gap: 0;
2977
3027
  margin: 0 0 40px 0;
2978
3028
  }
3029
+ .stats-pill.failed { color: var(--danger-dark); }
3030
+ .stats-pill.flaky { color: #4b5563; }
2979
3031
  .browser-breakdown {
2980
3032
  display: flex;
2981
3033
  flex-direction: column;
@@ -3420,12 +3472,19 @@ function generateHTML(reportData, trendData = null) {
3420
3472
  box-shadow: 0 4px 12px rgba(239, 68, 68, 0.2);
3421
3473
  }
3422
3474
  .summary-card.status-failed .value { color: #ef4444; }
3475
+ .summary-card.status-flaky::before { background: var(--neutral-500); }
3423
3476
  .summary-card.status-skipped { background: rgba(245, 158, 11, 0.02); }
3424
3477
  .summary-card.status-skipped:hover {
3425
3478
  background: rgba(245, 158, 11, 0.15);
3426
3479
  box-shadow: 0 4px 12px rgba(245, 158, 11, 0.2);
3427
3480
  }
3428
3481
  .summary-card.status-skipped .value { color: #f59e0b; }
3482
+ .summary-card.flaky-status { background: rgba(156, 163, 175, 0.05); }
3483
+ .summary-card.flaky-status:hover {
3484
+ background: rgba(156, 163, 175, 0.15);
3485
+ box-shadow: 0 4px 12px rgba(156, 163, 175, 0.2);
3486
+ }
3487
+ .summary-card.flaky-status .value { color: #64748b; }
3429
3488
  .summary-card:not([class*='status-']) .value { color: #0f172a; }
3430
3489
  .dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items: start; }
3431
3490
  .dashboard-column {
@@ -3525,6 +3584,7 @@ function generateHTML(reportData, trendData = null) {
3525
3584
  }
3526
3585
  .suite-card.status-passed::before { background: var(--success-color); }
3527
3586
  .suite-card.status-failed::before { background: var(--danger-color); }
3587
+ .suite-card.status-flaky::before { background: var(--neutral-500); }
3528
3588
  .suite-card.status-skipped::before { background: var(--warning-color); }
3529
3589
 
3530
3590
  /* Outcome Badge */
@@ -3570,8 +3630,9 @@ function generateHTML(reportData, trendData = null) {
3570
3630
  }
3571
3631
  .status-indicator-dot.status-passed { background-color: var(--success-color); box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.15); }
3572
3632
  .status-indicator-dot.status-failed { background-color: var(--danger-color); box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.15); }
3633
+ .status-indicator-dot.status-flaky { background-color: var(--neutral-500); box-shadow: 0 0 0 4px rgba(107, 114, 128, 0.15); }
3573
3634
  .status-indicator-dot.status-skipped { background-color: rgba(245, 158, 11, 0.1); color: var(--warning-dark); border: 1px solid rgba(245, 158, 11, 0.2); }
3574
- .status-flaky { background-color: rgba(234, 179, 8, 0.1); color: var(--warning-dark); border: 1px solid rgba(234, 179, 8, 0.2); }
3635
+ .status-flaky { background-color: #C0C0C0; color: #000000; border: 1px solid #A0A0A0; }
3575
3636
 
3576
3637
  .browser-tag {
3577
3638
  font-size: 0.8em;
@@ -3623,6 +3684,7 @@ function generateHTML(reportData, trendData = null) {
3623
3684
  .stat-pill svg { width: 14px; height: 14px; }
3624
3685
  .stat-pill.passed { color: var(--success-dark); }
3625
3686
  .stat-pill.failed { color: var(--danger-dark); }
3687
+ .stat-pill.flaky { color: #4b5563; }
3626
3688
  .stat-pill.skipped { color: var(--warning-dark); }
3627
3689
  .filters {
3628
3690
  display: flex;
@@ -4496,7 +4558,8 @@ function generateHTML(reportData, trendData = null) {
4496
4558
  <div class="summary-card status-skipped"><h3>Skipped</h3><div class="value">${
4497
4559
  runSummary.skipped || 0
4498
4560
  }</div><div class="trend-percentage">${skipPercentage}%</div></div>
4499
- <div class="summary-card"><h3>Avg. Test Time</h3><div class="value">${avgTestDuration}</div></div>
4561
+ <div class="summary-card flaky-status"><h3>Flaky</h3><div class="value">${runSummary.flaky || 0}</div>
4562
+ <div class="trend-percentage">${flakyPercentage}%</div></div>
4500
4563
  <div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
4501
4564
  runSummary.duration,
4502
4565
  )}</div></div>
@@ -4533,6 +4596,7 @@ function generateHTML(reportData, trendData = null) {
4533
4596
  [
4534
4597
  { label: "Passed", value: runSummary.passed },
4535
4598
  { label: "Failed", value: runSummary.failed },
4599
+ { label: "Flaky", value: runSummary.flaky || 0 },
4536
4600
  { label: "Skipped", value: runSummary.skipped || 0 },
4537
4601
  ],
4538
4602
  400,
@@ -4550,7 +4614,7 @@ function generateHTML(reportData, trendData = null) {
4550
4614
  <div id="test-runs" class="tab-content">
4551
4615
  <div class="filters">
4552
4616
  <input type="text" id="filter-name" placeholder="Filter by test name/path..." style="border-color: black; border-style: outset;">
4553
- <select id="filter-status"><option value="">All Statuses</option><option value="passed">Passed</option><option value="failed">Failed</option><option value="skipped">Skipped</option></select>
4617
+ <select id="filter-status"><option value="">All Statuses</option><option value="passed">Passed</option><option value="failed">Failed</option><option value="flaky">Flaky</option><option value="skipped">Skipped</option></select>
4554
4618
  <select id="filter-browser"><option value="">All Browsers</option>${Array.from(
4555
4619
  new Set(
4556
4620
  (results || []).map((test) => test.browser || "unknown"),
@@ -5308,6 +5372,7 @@ async function main() {
5308
5372
  passed: histRunReport.run.passed,
5309
5373
  failed: histRunReport.run.failed,
5310
5374
  skipped: histRunReport.run.skipped || 0,
5375
+ flaky: histRunReport.run.flaky || (histRunReport.results ? histRunReport.results.filter(r => r.status === 'flaky' || r.outcome === 'flaky').length : 0),
5311
5376
  });
5312
5377
 
5313
5378
  if (histRunReport.results && Array.isArray(histRunReport.results)) {
@@ -339,6 +339,12 @@ function generateTestTrendsChart(trendData) {
339
339
  color: "var(--warning-color)",
340
340
  marker: { symbol: "circle" },
341
341
  },
342
+ {
343
+ name: "Flaky",
344
+ data: runs.map((r) => r.flaky || 0),
345
+ color: "var(--neutral-500)",
346
+ marker: { symbol: "circle" },
347
+ },
342
348
  ];
343
349
  const runsForTooltip = runs.map((r) => ({
344
350
  runId: r.runId,
@@ -542,6 +548,9 @@ function generateTestHistoryChart(history) {
542
548
  case "skipped":
543
549
  color = "var(--warning-color)";
544
550
  break;
551
+ case "flaky":
552
+ color = "var(--neutral-500)";
553
+ break;
545
554
  default:
546
555
  color = "var(--dark-gray-color)";
547
556
  }
@@ -658,6 +667,9 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
658
667
  case "Failed":
659
668
  color = "var(--danger-color)";
660
669
  break;
670
+ case "Flaky":
671
+ color = "var(--neutral-500)";
672
+ break;
661
673
  case "Skipped":
662
674
  color = "var(--warning-color)";
663
675
  break;
@@ -904,7 +916,10 @@ function generateEnvironmentSection(environmentData) {
904
916
  }
905
917
 
906
918
  function generateEnvironmentDashboard(environment, hideHeader = false) {
907
- const cpuInfo = `model: ${environment.cpu.model}, cores: ${environment.cpu.cores}`;
919
+ const cpuModel = environment.cpu && environment.cpu.model ? environment.cpu.model : "N/A";
920
+ const cpuCores = environment.cpu && environment.cpu.cores ? environment.cpu.cores : "N/A";
921
+ const cpuInfo = `model: ${cpuModel}, cores: ${cpuCores}`;
922
+ const cwdInfo = environment.cwd || "N/A";
908
923
 
909
924
  return `
910
925
  <div class="env-modern-card${hideHeader ? " no-header" : ""}">
@@ -1294,7 +1309,7 @@ function generateEnvironmentDashboard(environment, hideHeader = false) {
1294
1309
  </div>
1295
1310
  <div class="env-item-content">
1296
1311
  <p class="env-item-label">Working Dir</p>
1297
- <div class="env-item-value" title="${environment.cwd}">${environment.cwd.length > 30 ? "..." + environment.cwd.slice(-27) : environment.cwd}</div>
1312
+ <div class="env-item-value" title="${cwdInfo}">${cwdInfo.length > 30 ? "..." + cwdInfo.slice(-27) : cwdInfo}</div>
1298
1313
  </div>
1299
1314
  </div>
1300
1315
  </div>
@@ -1323,11 +1338,11 @@ function generateWorkerDistributionChart(results) {
1323
1338
  const workerId =
1324
1339
  typeof test.workerId !== "undefined" ? test.workerId : "N/A";
1325
1340
  if (!acc[workerId]) {
1326
- acc[workerId] = { passed: 0, failed: 0, skipped: 0, tests: [] };
1341
+ acc[workerId] = { passed: 0, failed: 0, skipped: 0, flaky: 0, tests: [] };
1327
1342
  }
1328
1343
 
1329
1344
  const status = String(test.status).toLowerCase();
1330
- if (status === "passed" || status === "failed" || status === "skipped") {
1345
+ if (status === "passed" || status === "failed" || status === "skipped" || status === "flaky") {
1331
1346
  acc[workerId][status]++;
1332
1347
  }
1333
1348
 
@@ -1372,6 +1387,7 @@ function generateWorkerDistributionChart(results) {
1372
1387
  const passedData = workerIds.map((id) => workerData[id].passed);
1373
1388
  const failedData = workerIds.map((id) => workerData[id].failed);
1374
1389
  const skippedData = workerIds.map((id) => workerData[id].skipped);
1390
+ const flakyData = workerIds.map((id) => workerData[id].flaky);
1375
1391
 
1376
1392
  const categoriesString = JSON.stringify(categories);
1377
1393
  const fullDataString = JSON.stringify(fullWorkerData);
@@ -1379,6 +1395,7 @@ function generateWorkerDistributionChart(results) {
1379
1395
  { name: "Passed", data: passedData, color: "var(--success-color)" },
1380
1396
  { name: "Failed", data: failedData, color: "var(--danger-color)" },
1381
1397
  { name: "Skipped", data: skippedData, color: "var(--warning-color)" },
1398
+ { name: "Flaky", data: flakyData, color: "var(--neutral-500)" },
1382
1399
  ]);
1383
1400
 
1384
1401
  // The HTML now includes the chart container, the modal, and styles for the modal
@@ -1645,6 +1662,7 @@ function generateTestHistoryContent(trendData) {
1645
1662
  <option value="">All Statuses</option>
1646
1663
  <option value="passed">Passed</option>
1647
1664
  <option value="failed">Failed</option>
1665
+ <option value="flaky">Flaky</option>
1648
1666
  <option value="skipped">Skipped</option>
1649
1667
  </select>
1650
1668
  <button id="clear-history-filters" class="clear-filters-btn">Clear Filters</button>
@@ -1716,7 +1734,7 @@ function getStatusClass(status) {
1716
1734
  case "failed":
1717
1735
  return "status-failed";
1718
1736
  case "skipped":
1719
- return "⏭️";
1737
+ return "status-skipped";
1720
1738
  case "flaky":
1721
1739
  return "status-flaky";
1722
1740
  default:
@@ -1778,6 +1796,7 @@ function getSuitesData(results) {
1778
1796
  browser: browser,
1779
1797
  passed: 0,
1780
1798
  failed: 0,
1799
+ flaky: 0,
1781
1800
  skipped: 0,
1782
1801
  count: 0,
1783
1802
  statusOverall: "passed",
@@ -1785,12 +1804,15 @@ function getSuitesData(results) {
1785
1804
  }
1786
1805
  const suite = suitesMap.get(key);
1787
1806
  suite.count++;
1788
- const currentStatus = String(test.status).toLowerCase();
1807
+ let currentStatus = String(test.status).toLowerCase();
1808
+ if (test.outcome === 'flaky') currentStatus = 'flaky';
1789
1809
  if (currentStatus && suite[currentStatus] !== undefined) {
1790
1810
  suite[currentStatus]++;
1791
1811
  }
1792
1812
  if (currentStatus === "failed") suite.statusOverall = "failed";
1793
- else if (currentStatus === "skipped" && suite.statusOverall !== "failed")
1813
+ else if (currentStatus === "flaky" && suite.statusOverall !== "failed")
1814
+ suite.statusOverall = "flaky";
1815
+ else if (currentStatus === "skipped" && suite.statusOverall !== "failed" && suite.statusOverall !== "flaky")
1794
1816
  suite.statusOverall = "skipped";
1795
1817
  });
1796
1818
  return Array.from(suitesMap.values());
@@ -1873,6 +1895,10 @@ function generateSuitesWidget(suitesData) {
1873
1895
  <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg>
1874
1896
  ${suite.failed}
1875
1897
  </span>
1898
+ <span class="stat-pill flaky" title="Flaky">
1899
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>
1900
+ ${suite.flaky || 0}
1901
+ </span>
1876
1902
  <span class="stat-pill skipped" title="Skipped">
1877
1903
  <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>
1878
1904
  ${suite.skipped}
@@ -2255,6 +2281,7 @@ function generateSeverityDistributionChart(results) {
2255
2281
  const data = {
2256
2282
  passed: [0, 0, 0, 0, 0],
2257
2283
  failed: [0, 0, 0, 0, 0],
2284
+ flaky: [0, 0, 0, 0, 0],
2258
2285
  skipped: [0, 0, 0, 0, 0],
2259
2286
  };
2260
2287
 
@@ -2273,6 +2300,8 @@ function generateSeverityDistributionChart(results) {
2273
2300
  status === "interrupted"
2274
2301
  ) {
2275
2302
  data.failed[index]++;
2303
+ } else if (status === "flaky") {
2304
+ data.flaky[index]++;
2276
2305
  } else {
2277
2306
  data.skipped[index]++;
2278
2307
  }
@@ -2286,6 +2315,7 @@ function generateSeverityDistributionChart(results) {
2286
2315
  const seriesData = [
2287
2316
  { name: "Passed", data: data.passed, color: "var(--success-color)" },
2288
2317
  { name: "Failed", data: data.failed, color: "var(--danger-color)" },
2318
+ { name: "Flaky", data: data.flaky, color: "var(--neutral-500)" },
2289
2319
  { name: "Skipped", data: data.skipped, color: "var(--warning-color)" },
2290
2320
  ];
2291
2321
 
@@ -2429,17 +2459,14 @@ function generateHTML(reportData, trendData = null) {
2429
2459
  duration: 0,
2430
2460
  timestamp: new Date().toISOString(),
2431
2461
  };
2432
- const totalTestsOr1 = runSummary.totalTests || 1;
2433
- const passPercentage = Math.round((runSummary.passed / totalTestsOr1) * 100);
2434
- const failPercentage = Math.round((runSummary.failed / totalTestsOr1) * 100);
2435
- const skipPercentage = Math.round(
2436
- ((runSummary.skipped || 0) / totalTestsOr1) * 100,
2437
- );
2462
+
2438
2463
  const avgTestDuration =
2439
2464
  runSummary.totalTests > 0
2440
2465
  ? formatDuration(runSummary.duration / runSummary.totalTests)
2441
2466
  : "0.0s";
2442
2467
 
2468
+ const flakyCount = (results || []).filter(r => r.outcome === 'flaky').length;
2469
+
2443
2470
  // Calculate retry statistics
2444
2471
  const totalRetried = (results || []).reduce((acc, test) => {
2445
2472
  if (test.retryHistory && test.retryHistory.length > 0) {
@@ -2452,19 +2479,30 @@ function generateHTML(reportData, trendData = null) {
2452
2479
  let calculatedPassed = 0;
2453
2480
  let calculatedFailed = 0;
2454
2481
  let calculatedSkipped = 0;
2482
+ let calculatedFlaky = 0;
2455
2483
  let calculatedTotal = 0;
2456
2484
 
2457
2485
  (results || []).forEach(test => {
2458
2486
  calculatedTotal++;
2459
- // If retries exist and final_status is present, use it. Otherwise use test.status.
2460
- const statusToUse = (test.retryHistory && test.retryHistory.length > 0 && test.final_status)
2461
- ? test.final_status
2462
- : test.status;
2487
+ // New Logic: If outcome is 'flaky', it overrides everything.
2488
+ let statusToUse = test.status;
2489
+ if (test.outcome === 'flaky') {
2490
+ statusToUse = 'flaky';
2491
+ } else if (test.status === 'flaky') {
2492
+ // Just in case outcome wasn't set but status was (unlikely with new reporter)
2493
+ statusToUse = 'flaky';
2494
+ } else if (test.retryHistory && test.retryHistory.length > 0 && test.final_status) {
2495
+ statusToUse = test.final_status;
2496
+ }
2497
+
2498
+ // Update test status in place for charts
2499
+ test.status = statusToUse;
2463
2500
 
2464
2501
  const s = String(statusToUse).toLowerCase();
2465
2502
  if (s === 'passed') calculatedPassed++;
2466
2503
  else if (s === 'skipped') calculatedSkipped++;
2467
- else calculatedFailed++; // Treat everything else as failed for simplicity, or add specific checks
2504
+ else if (s === 'flaky') calculatedFlaky++;
2505
+ else calculatedFailed++; // failed, timedout, interrupted
2468
2506
  });
2469
2507
 
2470
2508
  // Override runSummary counts with our calculated ones if results exist
@@ -2472,9 +2510,18 @@ function generateHTML(reportData, trendData = null) {
2472
2510
  runSummary.passed = calculatedPassed;
2473
2511
  runSummary.failed = calculatedFailed;
2474
2512
  runSummary.skipped = calculatedSkipped;
2513
+ runSummary.flaky = calculatedFlaky;
2475
2514
  runSummary.totalTests = calculatedTotal;
2476
2515
  }
2477
2516
 
2517
+ const totalTestsOr1 = runSummary.totalTests || 1;
2518
+ const passPercentage = Math.round((runSummary.passed / totalTestsOr1) * 100);
2519
+ const failPercentage = Math.round((runSummary.failed / totalTestsOr1) * 100);
2520
+ const skipPercentage = Math.round(
2521
+ ((runSummary.skipped || 0) / totalTestsOr1) * 100,
2522
+ );
2523
+ const flakyPercentage = Math.round(((runSummary.flaky || 0) / totalTestsOr1) * 100);
2524
+
2478
2525
 
2479
2526
  // Calculate browser distribution
2480
2527
  const browserStats = (results || []).reduce((acc, test) => {
@@ -2876,7 +2923,7 @@ function generateHTML(reportData, trendData = null) {
2876
2923
  ? test.final_status
2877
2924
  : test.status;
2878
2925
 
2879
- const outcomeBadge = (test.outcome)
2926
+ const outcomeBadge = (test.outcome && test.outcome !== 'flaky')
2880
2927
  ? `<span class="outcome-badge ${test.outcome}">${test.outcome}</span>`
2881
2928
  : '';
2882
2929
 
@@ -3423,6 +3470,16 @@ function generateHTML(reportData, trendData = null) {
3423
3470
  .summary-card.status-skipped .value {
3424
3471
  color: #f59e0b;
3425
3472
  }
3473
+ .summary-card.flaky-status {
3474
+ background: rgba(156, 163, 175, 0.08);
3475
+ }
3476
+ .summary-card.flaky-status:hover {
3477
+ background: rgba(156, 163, 175, 0.15);
3478
+ box-shadow: 0 4px 12px rgba(156, 163, 175, 0.2);
3479
+ }
3480
+ .summary-card.flaky-status .value {
3481
+ color: #9ca3af;
3482
+ }
3426
3483
  .summary-card:not([class*='status-']) .value {
3427
3484
  color: #f9fafb;
3428
3485
  }
@@ -3545,6 +3602,7 @@ function generateHTML(reportData, trendData = null) {
3545
3602
  }
3546
3603
  .suite-card.status-passed::before { background: var(--success-color); }
3547
3604
  .suite-card.status-failed::before { background: var(--danger-color); }
3605
+ .suite-card.status-flaky::before { background: var(--neutral-500); }
3548
3606
  .suite-card.status-skipped::before { background: var(--warning-color); }
3549
3607
 
3550
3608
  .suite-card.status-skipped::before { background: var(--warning-color); }
@@ -3647,6 +3705,7 @@ function generateHTML(reportData, trendData = null) {
3647
3705
  .stat-pill svg { width: 14px; height: 14px; }
3648
3706
  .stat-pill.passed { color: var(--success-dark); }
3649
3707
  .stat-pill.failed { color: var(--danger-dark); }
3708
+ .stat-pill.flaky { color: #4b5563; }
3650
3709
  .stat-pill.skipped { color: var(--warning-dark); }
3651
3710
  color: #93c5fd;
3652
3711
  padding: 6px 12px;
@@ -3842,9 +3901,9 @@ function generateHTML(reportData, trendData = null) {
3842
3901
  background: var(--warning-color);
3843
3902
  }
3844
3903
  .status-badge.status-flaky {
3845
- background: rgba(234, 179, 8, 0.15);
3846
- color: var(--warning-dark);
3847
- border: 1px solid var(--warning-color);
3904
+ background-color: #C0C0C0;
3905
+ color: #000000;
3906
+ border: 1px solid #A0A0A0;
3848
3907
  }
3849
3908
  .status-badge.status-unknown {
3850
3909
  background: var(--dark-gray-color);
@@ -4480,7 +4539,8 @@ function generateHTML(reportData, trendData = null) {
4480
4539
  background-color: #f59e0b;
4481
4540
  }
4482
4541
  .status-badge-small.status-flaky {
4483
- background-color: #f59e0b;
4542
+ background-color: #C0C0C0;
4543
+ color: #000000;
4484
4544
  }
4485
4545
  .status-badge-small.status-unknown {
4486
4546
  background-color: var(--dark-gray-color);
@@ -6182,7 +6242,8 @@ function generateHTML(reportData, trendData = null) {
6182
6242
  <div class="summary-card status-skipped"><h3>Skipped</h3><div class="value">${
6183
6243
  runSummary.skipped || 0
6184
6244
  }</div><div class="trend-percentage">${skipPercentage}%</div></div>
6185
- <div class="summary-card"><h3>Avg. Test Time</h3><div class="value">${avgTestDuration}</div></div>
6245
+ <div class="summary-card flaky-status"><h3>Flaky</h3><div class="value">${runSummary.flaky || 0}</div>
6246
+ <div class="trend-percentage">${flakyPercentage}%</div></div>
6186
6247
  <div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
6187
6248
  runSummary.duration,
6188
6249
  )}</div></div>
@@ -6219,6 +6280,7 @@ function generateHTML(reportData, trendData = null) {
6219
6280
  [
6220
6281
  { label: "Passed", value: runSummary.passed },
6221
6282
  { label: "Failed", value: runSummary.failed },
6283
+ { label: "Flaky", value: runSummary.flaky || 0 },
6222
6284
  { label: "Skipped", value: runSummary.skipped || 0 },
6223
6285
  ],
6224
6286
  400,
@@ -6235,7 +6297,7 @@ function generateHTML(reportData, trendData = null) {
6235
6297
  <div id="test-runs" class="tab-content">
6236
6298
  <div class="filters" style="border-color: black; border-style: groove;">
6237
6299
  <input type="text" id="filter-name" placeholder="Filter by test name/path..." style="border-color: black; border-style: outset;">
6238
- <select id="filter-status"><option value="">All Statuses</option><option value="passed">Passed</option><option value="failed">Failed</option><option value="skipped">Skipped</option></select>
6300
+ <select id="filter-status"><option value="">All Statuses</option><option value="passed">Passed</option><option value="failed">Failed</option><option value="flaky">Flaky</option><option value="skipped">Skipped</option></select>
6239
6301
  <select id="filter-browser"><option value="">All Browsers</option>${Array.from(
6240
6302
  new Set(
6241
6303
  (results || []).map((test) => test.browser || "unknown"),
@@ -7175,6 +7237,7 @@ async function main() {
7175
7237
  passed: histRunReport.run.passed,
7176
7238
  failed: histRunReport.run.failed,
7177
7239
  skipped: histRunReport.run.skipped || 0,
7240
+ flaky: histRunReport.run.flaky || (histRunReport.results ? histRunReport.results.filter(r => r.status === 'flaky' || r.outcome === 'flaky').length : 0),
7178
7241
  });
7179
7242
 
7180
7243
  if (histRunReport.results && Array.isArray(histRunReport.results)) {
@@ -92,6 +92,7 @@ function mergeReports(shardDirs) {
92
92
  combinedRun.passed += run.passed || 0;
93
93
  combinedRun.failed += run.failed || 0;
94
94
  combinedRun.skipped += run.skipped || 0;
95
+ combinedRun.flaky = (combinedRun.flaky || 0) + (run.flaky || 0);
95
96
  combinedRun.duration += run.duration || 0;
96
97
 
97
98
  if (run.environment) {
@@ -228,7 +229,7 @@ function cleanupShardDirectories(shardDirs) {
228
229
 
229
230
  console.log(`\n✅ Merged report saved as ${OUTPUT_FILE}`);
230
231
  console.log(` Total tests: ${merged.run.totalTests}`);
231
- console.log(` Passed: ${merged.run.passed} | Failed: ${merged.run.failed} | Skipped: ${merged.run.skipped}`);
232
+ console.log(` Passed: ${merged.run.passed} | Failed: ${merged.run.failed} | Skipped: ${merged.run.skipped} | Flaky: ${merged.run.flaky}`);
232
233
 
233
234
  // 5. Cleanup Shard Directories
234
235
  cleanupShardDirectories(shardDirs);