collab/mermaid/scripts/compare-timings.tsblame
View source
6dd74de1/**
6dd74de2 * Compares new E2E test timings with previous timings and determines whether to keep the new timings.
6dd74de3 *
6dd74de4 * The script will:
6dd74de5 * 1. Read old timings from git HEAD
6dd74de6 * 2. Read new timings from the current file
6dd74de7 * 3. Compare the timings and specs
6dd74de8 * 4. Keep new timings if:
6dd74de9 * - Specs were added/removed
6dd74de10 * - Any timing changed by 20% or more
6dd74de11 * 5. Revert to old timings if:
6dd74de12 * - No significant timing changes
6dd74de13 *
6dd74de14 * This helps prevent unnecessary timing updates when test performance hasn't changed significantly.
6dd74de15 */
6dd74de16
6dd74de17import fs from 'node:fs';
6dd74de18import path from 'node:path';
6dd74de19import { execSync } from 'node:child_process';
6dd74de20
6dd74de21interface Timing {
6dd74de22 spec: string;
6dd74de23 duration: number;
6dd74de24}
6dd74de25
6dd74de26interface TimingsFile {
6dd74de27 durations: Timing[];
6dd74de28}
6dd74de29
6dd74de30interface CleanupOptions {
6dd74de31 keepNew: boolean;
6dd74de32 reason: string;
6dd74de33}
6dd74de34
6dd74de35const TIMINGS_FILE = 'cypress/timings.json';
6dd74de36const TIMINGS_PATH = path.join(process.cwd(), TIMINGS_FILE);
6dd74de37
6dd74de38function log(message: string): void {
6dd74de39 // eslint-disable-next-line no-console
6dd74de40 console.log(message);
6dd74de41}
6dd74de42
6dd74de43function readOldTimings(): TimingsFile {
6dd74de44 try {
6dd74de45 const oldContent = execSync(`git show HEAD:${TIMINGS_FILE}`, { encoding: 'utf8' });
6dd74de46 return JSON.parse(oldContent);
6dd74de47 } catch {
6dd74de48 log('Error getting old timings, using empty file');
6dd74de49 return { durations: [] };
6dd74de50 }
6dd74de51}
6dd74de52
6dd74de53function readNewTimings(): TimingsFile {
6dd74de54 return JSON.parse(fs.readFileSync(TIMINGS_PATH, 'utf8'));
6dd74de55}
6dd74de56
6dd74de57function cleanupFiles({ keepNew, reason }: CleanupOptions): void {
6dd74de58 if (keepNew) {
6dd74de59 log(`Keeping new timings: ${reason}`);
6dd74de60 } else {
6dd74de61 log(`Reverting to old timings: ${reason}`);
6dd74de62 execSync(`git checkout HEAD -- ${TIMINGS_FILE}`);
6dd74de63 }
6dd74de64}
6dd74de65
6dd74de66function compareTimings(): void {
6dd74de67 const oldTimings = readOldTimings();
6dd74de68 const newTimings = readNewTimings();
6dd74de69
6dd74de70 const oldSpecs = new Set(oldTimings.durations.map((d) => d.spec));
6dd74de71 const newSpecs = new Set(newTimings.durations.map((d) => d.spec));
6dd74de72
6dd74de73 // Check if specs were added or removed
6dd74de74 const addedSpecs = [...newSpecs].filter((spec) => !oldSpecs.has(spec));
6dd74de75 const removedSpecs = [...oldSpecs].filter((spec) => !newSpecs.has(spec));
6dd74de76
6dd74de77 if (addedSpecs.length > 0 || removedSpecs.length > 0) {
6dd74de78 log('Specs changed:');
6dd74de79 if (addedSpecs.length > 0) {
6dd74de80 log(`Added: ${addedSpecs.join(', ')}`);
6dd74de81 }
6dd74de82 if (removedSpecs.length > 0) {
6dd74de83 log(`Removed: ${removedSpecs.join(', ')}`);
6dd74de84 }
6dd74de85 return cleanupFiles({ keepNew: true, reason: 'Specs were added or removed' });
6dd74de86 }
6dd74de87
6dd74de88 // Check timing variations
6dd74de89 const timingChanges = newTimings.durations.map((newTiming) => {
6dd74de90 const oldTiming = oldTimings.durations.find((d) => d.spec === newTiming.spec);
6dd74de91 if (!oldTiming) {
6dd74de92 throw new Error(`Could not find old timing for spec: ${newTiming.spec}`);
6dd74de93 }
6dd74de94 const change = Math.abs(newTiming.duration - oldTiming.duration);
6dd74de95 const changePercent = change / oldTiming.duration;
6dd74de96 return { spec: newTiming.spec, change, changePercent };
6dd74de97 });
6dd74de98
6dd74de99 // Filter changes that's more than 5 seconds and 20% different
6dd74de100 const significantChanges = timingChanges.filter((t) => t.change > 5000 && t.changePercent >= 0.2);
6dd74de101
6dd74de102 if (significantChanges.length === 0) {
6dd74de103 log('No significant timing changes detected (threshold: 5s and 20%)');
6dd74de104 return cleanupFiles({ keepNew: false, reason: 'No significant timing changes' });
6dd74de105 }
6dd74de106
6dd74de107 log('Significant timing changes:');
6dd74de108 significantChanges.forEach((t) => {
6dd74de109 log(`${t.spec}: ${t.change.toFixed(1)}ms (${(t.changePercent * 100).toFixed(1)}%)`);
6dd74de110 });
6dd74de111
6dd74de112 cleanupFiles({ keepNew: true, reason: 'Significant timing changes detected' });
6dd74de113}
6dd74de114
6dd74de115compareTimings();