From cbcdf216fac3cfa92658654f4fe1472a7f5c8441 Mon Sep 17 00:00:00 2001 From: Adegbite Ayoade Date: Fri, 18 Sep 2026 02:51:23 +0100 Subject: [PATCH 1/3] fix(vscode): scope render tab assertions to the editor tab The render e2e tests asserted on the rendered model's tab title with a bare text selector. Recent VS Code builds show the active editor's name in the chat view as well, so the selector matches two elements and Playwright fails with a strict mode violation: locator('text=sushi.customers (rendered)') resolved to 2 elements: 1) sushi.customers (rendered) 2) sushi.customers (rendered) aka getByLabel('Enable current file context').locator('a') Match the editor tab by role instead, which ignores the chat entry. Verified against code-server 4.107.1 and 4.137.0. Signed-off-by: Adegbite Ayoade --- vscode/extension/tests/render.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vscode/extension/tests/render.spec.ts b/vscode/extension/tests/render.spec.ts index db660daae1..be0ad0fc41 100644 --- a/vscode/extension/tests/render.spec.ts +++ b/vscode/extension/tests/render.spec.ts @@ -38,7 +38,7 @@ test('Render works correctly', async ({ page, sharedCodeServer, tempDir }) => { // Check if the model is rendered by check if "`oi`.`order_id` AS `order_id`," is in the window await expect(page.locator('text="marketing"."customer_id" AS')).toBeVisible() - await expect(page.locator('text=sushi.customers (rendered)')).toBeVisible() + await expect(page.getByRole('tab', { name: 'sushi.customers (rendered)' })).toBeVisible() }) test('Render works correctly with model without a description', async ({ @@ -74,7 +74,7 @@ test('Render works correctly with model without a description', async ({ // Check if the model is rendered correctly await expect(page.locator('text="orders"."id" AS "id",')).toBeVisible() - await expect(page.locator('text=sushi.latest_order (rendered)')).toBeVisible() + await expect(page.getByRole('tab', { name: 'sushi.latest_order (rendered)' })).toBeVisible() }) test('Render works correctly with every rendered model opening a new tab', async ({ @@ -104,7 +104,7 @@ test('Render works correctly with every rendered model opening a new tab', async await runCommand(page, 'Render Model') // Check if the model is rendered correctly - await expect(page.locator('text=sushi.latest_order (rendered)')).toBeVisible() + await expect(page.getByRole('tab', { name: 'sushi.latest_order (rendered)' })).toBeVisible() // Open the customers model await page @@ -117,8 +117,8 @@ test('Render works correctly with every rendered model opening a new tab', async await runCommand(page, 'Render Model') // Assert both tabs exist - await expect(page.locator('text=sushi.latest_order (rendered)')).toBeVisible() - await expect(page.locator('text=sushi.customers (rendered)')).toBeVisible() + await expect(page.getByRole('tab', { name: 'sushi.latest_order (rendered)' })).toBeVisible() + await expect(page.getByRole('tab', { name: 'sushi.customers (rendered)' })).toBeVisible() }) test('Render shows model picker when no active editor is open', async ({ @@ -146,7 +146,7 @@ test('Render shows model picker when no active editor is open', async ({ await page.locator('text=sushi.customers').click() // Verify the rendered model is shown - await expect(page.locator('text=sushi.customers (rendered)')).toBeVisible({ + await expect(page.getByRole('tab', { name: 'sushi.customers (rendered)' })).toBeVisible({ timeout: 2_000, }) }) From a8dc91894c3f9ab5ca022c987132f5664aa00c63 Mon Sep 17 00:00:00 2001 From: Adegbite Ayoade Date: Mon, 21 Sep 2026 13:25:25 +0100 Subject: [PATCH 2/3] style(vscode): wrap the render tab assertions to prettier's width The role-based locators pushed those lines past the 80 column print width, which failed `pnpm run fmt:check`. Pure reformatting, no change in behaviour. Signed-off-by: Adegbite Ayoade --- vscode/extension/tests/render.spec.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/vscode/extension/tests/render.spec.ts b/vscode/extension/tests/render.spec.ts index be0ad0fc41..0fba008834 100644 --- a/vscode/extension/tests/render.spec.ts +++ b/vscode/extension/tests/render.spec.ts @@ -38,7 +38,9 @@ test('Render works correctly', async ({ page, sharedCodeServer, tempDir }) => { // Check if the model is rendered by check if "`oi`.`order_id` AS `order_id`," is in the window await expect(page.locator('text="marketing"."customer_id" AS')).toBeVisible() - await expect(page.getByRole('tab', { name: 'sushi.customers (rendered)' })).toBeVisible() + await expect( + page.getByRole('tab', { name: 'sushi.customers (rendered)' }), + ).toBeVisible() }) test('Render works correctly with model without a description', async ({ @@ -74,7 +76,9 @@ test('Render works correctly with model without a description', async ({ // Check if the model is rendered correctly await expect(page.locator('text="orders"."id" AS "id",')).toBeVisible() - await expect(page.getByRole('tab', { name: 'sushi.latest_order (rendered)' })).toBeVisible() + await expect( + page.getByRole('tab', { name: 'sushi.latest_order (rendered)' }), + ).toBeVisible() }) test('Render works correctly with every rendered model opening a new tab', async ({ @@ -104,7 +108,9 @@ test('Render works correctly with every rendered model opening a new tab', async await runCommand(page, 'Render Model') // Check if the model is rendered correctly - await expect(page.getByRole('tab', { name: 'sushi.latest_order (rendered)' })).toBeVisible() + await expect( + page.getByRole('tab', { name: 'sushi.latest_order (rendered)' }), + ).toBeVisible() // Open the customers model await page @@ -117,8 +123,12 @@ test('Render works correctly with every rendered model opening a new tab', async await runCommand(page, 'Render Model') // Assert both tabs exist - await expect(page.getByRole('tab', { name: 'sushi.latest_order (rendered)' })).toBeVisible() - await expect(page.getByRole('tab', { name: 'sushi.customers (rendered)' })).toBeVisible() + await expect( + page.getByRole('tab', { name: 'sushi.latest_order (rendered)' }), + ).toBeVisible() + await expect( + page.getByRole('tab', { name: 'sushi.customers (rendered)' }), + ).toBeVisible() }) test('Render shows model picker when no active editor is open', async ({ @@ -146,7 +156,9 @@ test('Render shows model picker when no active editor is open', async ({ await page.locator('text=sushi.customers').click() // Verify the rendered model is shown - await expect(page.getByRole('tab', { name: 'sushi.customers (rendered)' })).toBeVisible({ + await expect( + page.getByRole('tab', { name: 'sushi.customers (rendered)' }), + ).toBeVisible({ timeout: 2_000, }) }) From a5b7ffec6648f4405de0acede599b100ddcf6cce Mon Sep 17 00:00:00 2001 From: Adegbite Ayoade Date: Fri, 18 Sep 2026 17:19:17 +0100 Subject: [PATCH 3/3] fix(vscode): retry temp directory cleanup in the e2e fixture The per-test `tempDir` fixture removed the directory immediately after the test body finished. The extension host is often still writing into it at that point - the language server is shutting down, or uv is finishing a virtual environment the test created there - so `fs.remove` intermittently failed with `ENOTEMPTY` and failed an otherwise passing test, e.g. Error: ENOTEMPTY: directory not empty, rmdir '.../vscode-test-temp-Ay5Ma1/.venv/lib/python3.11/site-packages/tenacity' Retry the removal with a backoff and, if it still cannot be removed, warn instead of failing the test. Cleaning a temporary directory is not what the test is asserting. Signed-off-by: Adegbite Ayoade --- vscode/extension/tests/fixtures.ts | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/vscode/extension/tests/fixtures.ts b/vscode/extension/tests/fixtures.ts index 6ce92c3266..c96bb23044 100644 --- a/vscode/extension/tests/fixtures.ts +++ b/vscode/extension/tests/fixtures.ts @@ -8,6 +8,33 @@ import { CodeServerContext, } from './utils_code_server' +const REMOVE_RETRY_DELAYS_MS = [100, 250, 500, 1000, 2000] + +/** + * Remove a directory, retrying with a backoff. + * + * When a test finishes, the extension host may still be writing into its + * temporary directory - the language server is shutting down, or uv is + * finishing off a virtual environment it created there. A single `fs.remove` + * then fails with `ENOTEMPTY`, which fails an otherwise passing test. + */ +const removeWithRetry = async (dir: string): Promise => { + for (let attempt = 0; ; attempt++) { + try { + await fs.remove(dir) + return + } catch (error) { + if (attempt >= REMOVE_RETRY_DELAYS_MS.length) { + console.warn(`Failed to remove directory ${dir}:`, error) + return + } + await new Promise(resolve => + setTimeout(resolve, REMOVE_RETRY_DELAYS_MS[attempt]), + ) + } + } +} + // Worker-scoped fixture to start/stop VS Code server once per worker export const test = base.extend< // eslint-disable-next-line @typescript-eslint/no-empty-object-type @@ -52,7 +79,7 @@ export const test = base.extend< // Clean up after each test console.log(`Cleaning up temporary directory: ${tempDir}`) - await fs.remove(tempDir) + await removeWithRetry(tempDir) }, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error