fix(ci): stabilize Electron smoke flakiness at its sources
Windows CI / verify (push) Failing after 4m52s
Windows CI / verify (push) Failing after 4m52s
The workspace cutter drop check waited a fixed 250ms before asserting the populated cutter controls, but the drop triggers asynchronous IPC and media probing, so loaded runners regularly lost that race and failed with a misleading error. The check now polls the exact same UI conditions with a bounded deadline before asserting. All window screenshots in the cutter and workspace smokes carry an explicit generous timeout because large two-times-DPR captures exceeded the Playwright default under load. The focused Electron smoke and the cutter media matrix additionally retry exactly once in both CI workflows, mirroring the established packaging retries for transient runner turbulence, and the CI contract now enforces those retries instead of the former single-line steps.
This commit is contained in:
@@ -460,7 +460,7 @@ async function run() {
|
||||
&& emptyLayout.contained,
|
||||
`Empty cutter layout is stretched or displaced: ${JSON.stringify(emptyLayout)}`
|
||||
);
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'empty.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'empty.png') });
|
||||
await win.setViewportSize({ width: 2048, height: 1152 });
|
||||
const emptyFullscreenLayout = await win.evaluate(() => {
|
||||
const preview = document.getElementById('cutterPreview').getBoundingClientRect();
|
||||
@@ -485,7 +485,7 @@ async function run() {
|
||||
&& emptyFullscreenLayout.contained,
|
||||
`Fullscreen empty cutter layout is stretched or displaced: ${JSON.stringify(emptyFullscreenLayout)}`
|
||||
);
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'empty-fullscreen.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'empty-fullscreen.png') });
|
||||
await win.setViewportSize({ width: 1440, height: 900 });
|
||||
const emptyVolumeBefore = await win.evaluate(() => ({
|
||||
width: document.getElementById('cutterVolume').getBoundingClientRect().width,
|
||||
@@ -1333,7 +1333,7 @@ async function run() {
|
||||
`Short moving media did not show a sharp timeline quickly: ${JSON.stringify({ scrubFirstAssetsReadyMs, scrubFirstAssetQuality })}`
|
||||
);
|
||||
if (process.env.TWITCH_VOD_MANAGER_SCRUB_MEDIA) {
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'real-clip-ready.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'real-clip-ready.png') });
|
||||
await win.evaluate(() => window.updateCutterZoom(Number(document.getElementById('cutterZoom').max)));
|
||||
await win.waitForTimeout(500);
|
||||
realMaximumZoomState = await win.evaluate(() => {
|
||||
@@ -1349,7 +1349,7 @@ async function run() {
|
||||
};
|
||||
});
|
||||
check(realMaximumZoomState.renderedCount >= 150 && realMaximumZoomState.imageNodes === realMaximumZoomState.renderedCount && realMaximumZoomState.firstImageWidth <= 100, `Real maximum zoom still stretches thumbnail frames: ${JSON.stringify(realMaximumZoomState)}`);
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'real-clip-maximum-zoom.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'real-clip-maximum-zoom.png') });
|
||||
await win.evaluate(() => window.updateCutterZoom(1));
|
||||
}
|
||||
await win.evaluate(async () => {
|
||||
@@ -1538,7 +1538,7 @@ async function run() {
|
||||
]));
|
||||
const mediumFirstAssetsReadyMs = Date.now() - mediumLoadStarted;
|
||||
if (process.env.TWITCH_VOD_MANAGER_MEDIUM_MEDIA) {
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'medium-clip-ready.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'medium-clip-ready.png') });
|
||||
}
|
||||
check(mediumWaveformReadyMs < 1000, `The full-resolution 58-second waveform was not independently ready within one second: ${mediumWaveformReadyMs}ms`);
|
||||
const mediumZoomReuseBefore = await win.evaluate(() => ({
|
||||
@@ -1551,7 +1551,7 @@ async function run() {
|
||||
await win.evaluate(() => window.updateCutterZoom(Number(document.getElementById('cutterZoom').max)));
|
||||
await win.waitForTimeout(500);
|
||||
if (process.env.TWITCH_VOD_MANAGER_MEDIUM_MEDIA) {
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'medium-clip-maximum-zoom.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'medium-clip-maximum-zoom.png') });
|
||||
}
|
||||
const mediumZoomReuseAfter = await win.evaluate(() => ({
|
||||
thumbnailSource: document.querySelector('#cutterThumbnailStrip img').src,
|
||||
@@ -2137,7 +2137,7 @@ async function run() {
|
||||
volumeWidth: document.getElementById('cutterVolume').getBoundingClientRect().width,
|
||||
timeLeft: document.querySelector('.cutter-player-time').getBoundingClientRect().left
|
||||
}));
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'volume-expanded.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'volume-expanded.png') });
|
||||
check(expandedVolumeLayout.controlsWidth <= expandedVolumeLayout.clientWidth + 1 && expandedVolumeLayout.documentOverflow <= 1, `Expanded volume control causes overflow: ${JSON.stringify(expandedVolumeLayout)}`);
|
||||
check(focusedVolumeAppearance.active && focusedVolumeAppearance.appearance === 'none' && focusedVolumeAppearance.borderTopWidth === 0 && (focusedVolumeAppearance.outlineStyle === 'none' || focusedVolumeAppearance.outlineWidth === 0), `Focused volume control shows a rectangular native outline: ${JSON.stringify(focusedVolumeAppearance)}`);
|
||||
check(
|
||||
@@ -2322,7 +2322,7 @@ async function run() {
|
||||
};
|
||||
});
|
||||
check(assetDensity.zoom >= 8 && assetDensity.devicePixelRatio === 2 && assetDensity.thumbnailCount >= 190 && assetDensity.imageNodes === assetDensity.renderedFrames && assetDensity.renderedFrames >= 150 && assetDensity.renderedFrameIndexes.length === assetDensity.renderedFrames && assetDensity.renderedFrameIndexes[0] === 0 && assetDensity.renderedFrameIndexes.at(-1) === assetDensity.thumbnailCount - 1 && new Set(assetDensity.renderedFrameIndexes).size === assetDensity.renderedFrames && assetDensity.framePixelWidth > 0 && assetDensity.framePixelWidth <= assetDensity.sourceFrameWidth && assetDensity.sourceFrameWidth >= 320 && assetDensity.sourceFrameHeight >= 180 && assetDensity.canvasPixelWidth >= assetDensity.canvasCssWidth * 2 - 1 && assetDensity.waveformNaturalWidth >= assetDensity.targetWidth * 0.95 && assetDensity.waveformVerticalDensity >= 1 && assetDensity.waveformFilter === 'none' && assetDensity.waveformOpacity === 1 && assetDensity.trackEdgeError <= 1 && assetDensity.cutTimeError <= 1.5, `Timeline media is being upscaled, filtered or misaligned: ${JSON.stringify(assetDensity)}`);
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'maximum-zoom.png') });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'maximum-zoom.png') });
|
||||
const maximumZoomGeometryDelta = Math.max(...Object.keys(wheelZoomBefore.geometry).flatMap((key) => {
|
||||
const before = wheelZoomBefore.geometry[key];
|
||||
const after = assetDensity.geometry[key];
|
||||
@@ -2458,7 +2458,7 @@ async function run() {
|
||||
document.getElementById('cutterTab').scrollTop = 0;
|
||||
});
|
||||
await win.waitForTimeout(250);
|
||||
await win.screenshot({ path: path.join(cutterArtifactDir, 'editor.png'), fullPage: true });
|
||||
await win.screenshot({ timeout: 120000, path: path.join(cutterArtifactDir, 'editor.png'), fullPage: true });
|
||||
const multiAudio = await verifyMultiAudioExport(win, environment, multiAudioInputFile, multiAudioOutputFile);
|
||||
const multiAudioSelection = multiAudio.selection;
|
||||
const multiAudioExportResult = multiAudio.exportResult;
|
||||
|
||||
Reference in New Issue
Block a user