diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d38a27ccec..861bf185ab 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,11 +7,12 @@ on: jobs: pr: - runs-on: ${{ matrix.os }} + runs-on: ubuntu-24.04 strategy: fail-fast: false + max-parallel: 1 matrix: - os: [ubuntu-24.04, macos-26, windows-2022] + attempt: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] timeout-minutes: 240 steps: - uses: actions/checkout@v7 @@ -51,49 +52,13 @@ jobs: - run: npm run type-check - run: npm run lint - run: npm run build - - run: npx lerna run test --since origin/main - - name: Run headless test (with videos) - if: matrix.os != 'macos-26' + - name: Run quick-pick-color-theme attempt ${{ matrix.attempt }} uses: coactions/setup-xvfb@v1 with: - run: node packages/cli/bin/test.js --cwd packages/e2e --record-video - - name: Run headless test - uses: coactions/setup-xvfb@v1 - with: - run: npm run e2e - - name: Run headless test (flakyness check) - if: matrix.os != 'macos-26' - uses: coactions/setup-xvfb@v1 - with: - run: npm run e2e:check-flakyness - - name: Run headless test (second flakyness check) - if: matrix.os != 'macos-26' - uses: coactions/setup-xvfb@v1 - with: - run: npm run e2e:check-flakyness - - name: Run headless test (check leaks, event listener count) - if: matrix.os != 'macos-26' - uses: coactions/setup-xvfb@v1 - with: - run: node packages/cli/bin/test.js --cwd packages/e2e --check-leaks --measure-after - - name: Run headless test (check leaks, event listeners) - if: matrix.os == 'ubuntu-24.04' - uses: coactions/setup-xvfb@v1 - with: - run: node packages/cli/bin/test.js --cwd packages/e2e --check-leaks --measure event-listeners - - name: Run Memory City smoke test - if: matrix.os == 'ubuntu-24.04' - uses: coactions/setup-xvfb@v1 - with: - run: node packages/cli/bin/test.js --cwd packages/e2e --check-leaks --measure memory-city --only ^editor-open.ts --workers 1 - - name: Generate charts - run: node packages/charts/src/main.ts - - name: Validate Memory City artifact - if: matrix.os == 'ubuntu-24.04' - run: node packages/visualizations/src/validate.ts + run: node packages/cli/bin/test.js --cwd packages/e2e --only quick-pick-color-theme.ts --run-skipped-tests-anyway --record-video - uses: actions/upload-artifact@v7 if: always() with: - name: vscode-videos-${{ runner.os }} + name: quick-pick-color-theme-attempt-${{ matrix.attempt }} path: ./.vscode-videos include-hidden-files: true diff --git a/packages/e2e/src/quick-pick-color-theme.ts b/packages/e2e/src/quick-pick-color-theme.ts index 3f57ff0f7a..ca41f8c9b1 100644 --- a/packages/e2e/src/quick-pick-color-theme.ts +++ b/packages/e2e/src/quick-pick-color-theme.ts @@ -6,13 +6,77 @@ export const setup = async ({ QuickPick }: TestContext): Promise => { await QuickPick.showColorTheme() } -export const run = async ({ Colors, QuickPick, Workbench }: TestContext): Promise => { - await Workbench.shouldHaveEditorBackground(Colors.DarkModern) +interface ThemeState { + readonly background: string + readonly label: string +} + +const getEditorBackground = async (Workbench: TestContext['Workbench']): Promise => { + const background = await Workbench.evaluate({ + expression: `getComputedStyle(document.querySelector('.monaco-workbench')).getPropertyValue('--vscode-editor-background')`, + returnByValue: true, + }) + if (typeof background !== 'string') { + throw new Error(`Expected editor background to be a string`) + } + return background +} + +const waitForEditorBackground = async ( + Workbench: TestContext['Workbench'], + previousBackground: string, + expectedBackground = '', +): Promise => { + const background = await Workbench.evaluate({ + awaitPromise: true, + expression: `new Promise((resolve, reject) => { + const workbench = document.querySelector('.monaco-workbench') + const getBackground = () => getComputedStyle(workbench).getPropertyValue('--vscode-editor-background') + const deadline = performance.now() + 1000 + const check = () => { + const current = getBackground() + if (${JSON.stringify(expectedBackground)} ? current === ${JSON.stringify(expectedBackground)} : current !== ${JSON.stringify(previousBackground)}) { + resolve(current) + } else if (performance.now() >= deadline) { + reject(new Error('Editor background remained "' + current + '"')) + } else { + requestAnimationFrame(check) + } + } + check() + })`, + returnByValue: true, + }) + if (typeof background !== 'string') { + throw new Error(`Expected editor background to be a string`) + } + return background +} + +const getThemeState = async ({ QuickPick, Workbench }: TestContext, background = ''): Promise => { + const label = await QuickPick.getFocusedItemLabel() + return { + background: background || (await getEditorBackground(Workbench)), + label, + } +} + +export const run = async (context: TestContext): Promise => { + const { QuickPick, Workbench } = context + const initial = await getThemeState(context) + await QuickPick.focusNext() - await Workbench.shouldHaveEditorBackground(Colors.DarkPlus) + const nextBackground = await waitForEditorBackground(Workbench, initial.background) + const next = await getThemeState(context, nextBackground) + await QuickPick.focusNext() - await Workbench.shouldHaveEditorBackground(Colors.KimbieDark) + await waitForEditorBackground(Workbench, next.background) + await QuickPick.focusPrevious() await QuickPick.focusPrevious() - await Workbench.shouldHaveEditorBackground(Colors.DarkModern) + const restoredBackground = await waitForEditorBackground(Workbench, next.background, initial.background) + const restored = await getThemeState(context, restoredBackground) + if (restored.label !== initial.label || restored.background !== initial.background) { + throw new Error(`Expected color theme "${initial.label}" with background "${initial.background}" to be restored`) + } }