Skip to content

Commit 670a6d4

Browse files
authored
test: fix e2e web settings issues locator W-21754661 (#7069)
* test: fix e2e web settings issues locator * chore: comment cleanup * test: e2e web settings for newer vscode versions * docs: e2e test running/iteration * test: e2e windows settings overlay close * test: settings close after upsert
1 parent dd5597e commit 670a6d4

File tree

4 files changed

+42
-10
lines changed

4 files changed

+42
-10
lines changed

.claude/skills/playwright-e2e/references/iterating-playwright-tests.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ Scratch org setup: see `references/local-setup.md`.
1111
1. run `web` locally (use `--retries 0`, follow debugging tips)
1212
2. run `desktop` locally (use `--retries 0`)
1313

14-
**Passing args:** Use `--` to forward params to the underlying command, e.g. `npm run test:web -w <package> -- --retries 0` or `npm run test:desktop -w <package> -- --retries 0` 3. edit github workflows if needed 4. CI (windows, gha) - see `analyze-e2e.md` for monitoring and analyzing results
14+
**Passing args:** Use `--` to forward params to the underlying playwright command. Prefer file path over `--grep` (exact match, immune to title changes):
15+
```bash
16+
WIREIT_CACHE=none npm run test:web -w <package> -- --retries 0 test/playwright/specs/myTest.headless.spec.ts
17+
WIREIT_CACHE=none npm run test:desktop -w <package> -- --retries 0 test/playwright/specs/myTest.headless.spec.ts
18+
```
19+
`WIREIT_CACHE=none` is always required when running a subset — wireit's cache key is based on input file fingerprints, not CLI args, so it serves the cached full-suite result otherwise.
20+
21+
**Port conflict (web):** If a previous web server is still on port 3001, playwright fails immediately with `http://localhost:3001 is already used`. Fix: `lsof -ti :3001 | xargs kill -9`
22+
23+
3. edit github workflows if needed
24+
4. CI (windows, gha) - see `analyze-e2e.md` for monitoring and analyzing results
1525

1626
After passing, clean up while keeping tests passing:
1727

packages/playwright-vscode-ext/src/pages/settings.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { saveScreenshot } from '../shared/screenshotUtils';
1111
import {
1212
waitForVSCodeWorkbench,
1313
closeWelcomeTabs,
14+
closeSettingsTab,
1415
waitForWorkspaceReady,
1516
isMacDesktop,
1617
isDesktop
@@ -120,15 +121,23 @@ export const upsertSettings = async (page: Page, settings: Record<string, string
120121
// Wait for search results to appear - wait for any search result element to indicate search completed
121122
await page.locator('[data-id^="searchResultModel_"]').first().waitFor({ state: 'attached', timeout: 15_000 });
122123

123-
// Deterministic locator: target the element that actually contains the `data-id` attribute
124-
// VS Code only replaces the FIRST dot with underscore in data-id
125-
// e.g., "salesforcedx-vscode-metadata.deployOnSave.enabled" -> "searchResultModel_salesforcedx-vscode-metadata_deployOnSave.enabled"
124+
// VS Code sanitizeId(key) builds the data-id, but the behavior changed in 1.113:
125+
// < 1.113: replace(/[\.\/]/, '_') — only the FIRST dot replaced
126+
// e.g. "a.b.c" → "searchResultModel_a_b.c"
127+
// ≥ 1.113: replace(/[\.\/]/g, '_') — ALL dots replaced
128+
// e.g. "a.b.c" → "searchResultModel_a_b_c"
129+
// Use an OR selector so this works across VS Code versions.
126130
// Use .last() when duplicates exist (User + Workspace): we're on Workspace tab, so Workspace row is last
127-
const searchResultId = `searchResultModel_${id.replace(/\./, '_')}`;
128-
const row = page.locator(`[data-id="${searchResultId}"]`).last();
131+
const allDotsId = `searchResultModel_${id.replaceAll('.', '_')}`;
132+
const firstDotId = `searchResultModel_${id.replace('.', '_')}`;
133+
const dataIdSelector =
134+
allDotsId === firstDotId
135+
? `[data-id="${allDotsId}"]`
136+
: `[data-id="${allDotsId}"], [data-id="${firstDotId}"]`;
137+
const row = page.locator(dataIdSelector).last();
129138

130139
if (debugAria) {
131-
console.log(`[upsertSettings] using deterministic locator for ${id}: data-id="${searchResultId}"`);
140+
console.log(`[upsertSettings] using deterministic locator for ${id}: selector="${dataIdSelector}"`);
132141
// Capture HTML to debug selector issues - look for settings results
133142
try {
134143
const settingsBody = page.locator('.settings-body, .settings-tree-container, [class*="settings"]').first();
@@ -220,4 +229,7 @@ export const upsertSettings = async (page: Page, settings: Record<string, string
220229
} catch {}
221230
}
222231
}
232+
233+
// Close the settings overlay/tab so callers can open command palette etc.
234+
await closeSettingsTab(page);
223235
};

packages/playwright-vscode-ext/src/utils/helpers.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,13 @@ export const closeWelcomeTabs = async (page: Page): Promise<void> => {
200200
}).toPass({ timeout: 30_000 });
201201
};
202202

203-
/** Closes any visible Settings tabs */
203+
/** Closes any visible Settings tabs or the floating Settings overlay on Windows desktop */
204204
export const closeSettingsTab = async (page: Page): Promise<void> => {
205+
if (isWindowsDesktop()) {
206+
// On Windows desktop, Settings opens as a floating overlay dialog, not a tab
207+
await page.keyboard.press('Escape');
208+
return;
209+
}
205210
const settingsTab = page
206211
.locator(TAB)
207212
.filter({ hasText: /Settings/i })

packages/playwright-vscode-ext/test/playwright/specs/settings.headless.spec.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,13 @@ test.describe.serial('Settings', () => {
9898
await page.keyboard.press('Backspace');
9999
await page.keyboard.type(settingKey);
100100

101-
const searchResultId = `searchResultModel_${settingKey.replace(/\./, '_')}`;
102-
const row = page.locator(`[data-id="${searchResultId}"]`).last();
101+
const allDotsId = `searchResultModel_${settingKey.replaceAll('.', '_')}`;
102+
const firstDotId = `searchResultModel_${settingKey.replace('.', '_')}`;
103+
const dataIdSelector =
104+
allDotsId === firstDotId
105+
? `[data-id="${allDotsId}"]`
106+
: `[data-id="${allDotsId}"], [data-id="${firstDotId}"]`;
107+
const row = page.locator(dataIdSelector).last();
103108
await row.waitFor({ state: 'visible', timeout: 15_000 });
104109
const minimapCheckbox = row.getByRole('checkbox').first();
105110
await expect(minimapCheckbox).not.toBeChecked();

0 commit comments

Comments
 (0)