Skip to content

Commit 3ee0df9

Browse files
committed
Merge remote-tracking branch 'origin/develop' into wt-7537
2 parents e0431c6 + b2b23d7 commit 3ee0df9

93 files changed

Lines changed: 3038 additions & 424 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/plans/W-23073729.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# W-23073729 — E2E flake: LWC/Aura 2nd rename (editor context-menu) race
2+
3+
## Context
4+
5+
2nd rename via editor context-menu intermittently no-ops; renamed treeitem never appears.
6+
7+
- LWC `lwcRename.headless.spec.ts` retryRate 86% (flakes thru `toPass(20s)`).
8+
- Aura `auraRename.desktop.spec.ts` HARD-FAILS — LWC mitigations never back-ported.
9+
10+
Already-fixed LWC (lwcRename:92-108):
11+
12+
- atomic `activeQuickInputTextField(page).fill(finalName, { force: true })` (line 95)
13+
- `toPass({ timeout: 20_000 })` poll on renamed treeitem (line 106)
14+
15+
Aura old racy (auraRename:82-96):
16+
17+
- `ControlOrMeta+a` + `keyboard.type` (lines 85-86) — select-all/type focus race
18+
- `toBeVisible({ timeout: 10_000 })`, no poll (line 96)
19+
20+
Shared root: `executeEditorContextMenuCommand` (contextMenu.ts:128) opens editor menu by `data-uri` substring (openEditorContextMenu:13-41). The fileName match is a **synchronous for-loop snapshot** (`count()` then `getAttribute` per editor, :22-35): it reads the editor list ONCE with no retry. For static-path callers (deploy/retrieve/manifest pass `manifest/package.xml`, `${className}.cls` — editor already open) editor exists instantly, snapshot finds it. For the 2nd-rename callers, the renamed `data-uri` is still settling when the snapshot runs → no match (throws) or matches a stale tab. Plus `selectContextMenuItem` keyboard-Enter then a best-effort menu-hidden wait whose catch (:122) hides whether the command actually fired.
21+
22+
### Caller inventory (must not regress)
23+
24+
`executeEditorContextMenuCommand` callers — only the fileName-match path changes; the no-fileName `.first()` path (apex-oas :53) is untouched:
25+
26+
- static-path (editor pre-open, expects instant match): `deploySourcePath`, `deployManifest`, `generateManifest`, `retrieveInManifest` (x2), `nonTrackingOrgDeployRetrieveManifest` (x2) — all in `salesforcedx-vscode-metadata`
27+
- no-fileName (`.first()`): `apex-oas/contextMenuEditor` (:53), `playwright-vscode-ext/contextMenu.headless` (:34)
28+
- settling-URI (the flake): `lwcRename` (:93), `auraRename` (:83)
29+
30+
`selectContextMenuItem` is also reached by `executeExplorerContextMenuCommand` (all explorer-menu callers) — any change here is global.
31+
32+
Files:
33+
34+
- `packages/salesforcedx-vscode-lightning/test/playwright/specs/auraRename.desktop.spec.ts`
35+
- `packages/playwright-vscode-ext/src/pages/contextMenu.ts`
36+
- (ref only) `packages/salesforcedx-vscode-lwc/test/playwright/specs/lwcRename.headless.spec.ts`
37+
38+
Helpers: `activeQuickInputTextField` (quickInput.ts:21), `EDITOR_WITH_URI`/`CONTEXT_MENU` (locators.ts).
39+
40+
## Phases
41+
42+
### Phase 1 — harden openEditorContextMenu fileName match (settling URI only)
43+
44+
`contextMenu.ts:13-50`. Goal: settling renamed URI → bounded wait; static-path callers → instant match; `selectContextMenuItem` behavior unchanged.
45+
46+
**Match: auto-retrying locator (not expect.poll).** CSS attribute-substring locators auto-retry: instant when open, bounded wait when settling — the exact behavioral split the findings require, without changing static-path callers.
47+
48+
- delete the for-loop (:22-41); build the target as one locator:
49+
`const editor = fileName ? page.locator(`${EDITOR_WITH_URI}${cssAttrSubstring('data-uri', fileName)}`).first() : page.locator(EDITOR_WITH_URI).first();`
50+
where `data-uri*="<escaped>"` mirrors the old `dataUri.includes(fileName)` substring semantics. fileName values are paths (`manifest/package.xml`, `${newName}.js`, `${newName}.cmp`) — escape `"`/`\` for the CSS string, or use Playwright `getByXxx`-style escaping; no fileName contains quotes today but escape anyway.
51+
- keep the existing `await editor.waitFor({ state: 'visible', timeout: 10_000 })` (:45) — this is now the single auto-retry point: instant for static-path callers (editor already visible), waits up-to-10s for the renamed URI to attach+settle.
52+
- timeout semantics to document in the plan/PR: static-path callers resolve on first poll (no added latency); settling-URI callers retry up to 10s then throw a clear "editor not visible" error. The OLD error string ("No editor found … Available data-uris") is lost — preserve diagnostics by catching the `waitFor` timeout and rethrowing with `${fileName}` + a list of currently-present `data-uri`s (one extra `count()`/`getAttribute` pass, only on failure).
53+
- the `EDITOR_WITH_URI.first().waitFor` pre-wait (:16-17) becomes redundant (the combined locator's waitFor covers it) — remove it to avoid a stale first-editor wait masking the targeted wait.
54+
55+
**Menu-fired signal: do NOT throw inside the shared helper.** Findings are correct that dropping the catch at :122 throws for any instant-close caller (explorer + editor, 10+ specs) and there is no in-helper way to distinguish "closed instantly = OK" from "never opened = fail". The helper has no caller-specific success signal to assert on. So:
56+
57+
- leave `selectContextMenuItem` menu-hidden wait + catch (:122-124) unchanged (still best-effort, non-throwing) — global safety, no regression risk.
58+
- move the real "command fired" assertion to the rename callers, which DO have a deterministic side-effect: the rename quick-input widget. Both rename specs already call `await activeQuickInputWidget(page).waitFor({ state: 'attached', timeout: 10_000 })` immediately after (lwcRename:94, auraRename:84) — that IS the assertion that the editor menu fired. Phase 2 ensures aura has it (it does) and that it fails loud (it already throws on timeout). No new assertion needed; document that the quick-input wait is the menu-fired proof.
59+
60+
commit: `test(e2e): editor context-menu match waits for settling URI - W-23073729`
61+
62+
### Phase 2 — back-port LWC mitigations to Aura
63+
64+
`auraRename.desktop.spec.ts:82-96`
65+
66+
- line 85-86 → atomic fill: replace `ControlOrMeta+a` + `keyboard.type(finalName)` with `activeQuickInputTextField(page).fill(finalName, { force: true })`; import `activeQuickInputTextField`
67+
- line 96 → `toPass({ timeout: 20_000 })` poll on `finalFolder` visible (mirror lwcRename:106-108)
68+
- optionally harden 1st-rename block (63-64) same way for consistency — only if low-risk
69+
70+
commit: `test(lightning): back-port lwc rename mitigations to aura 2nd rename - W-23073729`
71+
72+
## Skills to apply
73+
74+
- playwright-e2e (running/iterating/analyze CI)
75+
- analyze-e2e (CI run inspection)
76+
- concise (any md)
77+
- verification
78+
- typescript
79+
80+
## Verification
81+
82+
- Run lwc + aura rename specs locally, repeat N to confirm flake gone: `npm run test:desktop` in salesforcedx-vscode-lightning (Aura) + salesforcedx-vscode-lwc (LWC).
83+
- Run every fileName-path caller spec (REQUIRED — helper change is global): prove locator swap keeps instant-match for pre-open editors. Rename specs alone insufficient.
84+
- `salesforcedx-vscode-metadata`: `deploySourcePath`, `deployManifest`, `generateManifest`, `retrieveInManifest`, `nonTrackingOrgDeployRetrieveManifest`.
85+
- No-fileName path sanity: `apex-oas/contextMenuEditor`, `playwright-vscode-ext/contextMenu.headless`.
86+
- Any added latency or "not visible" throw → substring-escape or `.first()` selection wrong; fix before merge.
87+
- Typecheck/lint `playwright-vscode-ext`: `npm run compile` + eslint.
88+
- Confirm `selectContextMenuItem` byte-for-byte unchanged (explorer callers depend on instant-close tolerance).

.claude/plans/W-23094890.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# W-23094890 — Enable unicorn/no-boolean-sort-comparator
2+
3+
## Context
4+
5+
- Enable `unicorn/no-boolean-sort-comparator` in `eslint.config.mjs`.
6+
- Rule: bans comparators returning boolean (yields 1/0, never negative → wrong sort).
7+
- Plugin `eslint-plugin-unicorn@68.0.0` already installed; rule file present (`node_modules/eslint-plugin-unicorn/rules/no-boolean-sort-comparator.js`).
8+
- Unicorn rule block already on `origin/develop` (`git show origin/develop:eslint.config.mjs` shows `unicorn/no-array-sort` / `unicorn/no-empty-file`). No blocker; proceed.
9+
- Scanned all `packages/*/src` + `test` w/ rule temporarily on: **0 violations**.
10+
- No code fixes needed — config-only change.
11+
- Note: `.sort((a,b)=> a>b ? 1 : -1)` (lwcTestController) is ternary, not boolean → rule allows.
12+
13+
## Phases
14+
15+
### Phase 1 — enable rule
16+
17+
- `eslint.config.mjs`: add `'unicorn/no-boolean-sort-comparator': 'error',` alphabetically — after `'unicorn/no-array-sort'`, before `'unicorn/no-empty-file'`. (Lines 171-172 in worktree; re-confirm via grep after rebase on develop vs trusting line numbers.)
18+
- No source changes (0 violations).
19+
- Commit: `chore(lint): enable unicorn/no-boolean-sort-comparator - W-23094890`
20+
- Files: `eslint.config.mjs`
21+
22+
## Skills
23+
24+
- typescript
25+
- packageJson — n/a (no dep change, plugin present)
26+
- verification
27+
28+
## Verification
29+
30+
- Unicorn block on develop: `git show origin/develop:eslint.config.mjs | grep -c unicorn/` > 0.
31+
- `npm run lint` clean (or scoped per-package eslint).
32+
- Confirms 0 violations introduced.
33+
- Not e2e-covered (lint-config change; CI lint gate covers).

.claude/plans/W-23094891.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# W-23094891 — Enable unicorn/no-chained-comparison
2+
3+
## Context
4+
5+
- Enable `unicorn/no-chained-comparison`: bans `a < b < c` (parses `(a < b) < c`, almost always bug).
6+
- Rule: eslint-plugin-unicorn v68, `hasSuggestions: true`, NOT auto-fixable (`--fix` won't touch).
7+
- Verified: 0 violations across `packages/**/*.ts` with rule enabled via config (inserted rule into `eslint.config.mjs`, ran `npx eslint 'packages/**/*.ts' -f json`, parsed for ruleId `unicorn/no-chained-comparison` → 0). NOT via `--rule` CLI flag (does not load the plugin → "could not find plugin unicorn"). Pure config change, no code edits.
8+
- No real dependency. Unicorn plugin infra (import line 19 + `unicorn:` plugin registration + existing rules) is already on `develop`. W-23094890 (`unicorn/no-boolean-sort-comparator`, commit 6b3e3d39d) is a sibling rule-enable, NOT a prerequisite, and is NOT merged to develop — independent, no ordering needed.
9+
- Config: `eslint.config.mjs`, `**/*.ts` block, unicorn rules list (lines 166-210, alphabetical).
10+
- Insert alphabetically between `no-array-sort` (171) and `no-empty-file` (172).
11+
12+
## Phases
13+
14+
### Phase 1 — enable rule
15+
16+
- Add `'unicorn/no-chained-comparison': 'error',` after line 171 in `eslint.config.mjs`.
17+
- Files: `eslint.config.mjs`
18+
- Commit: `chore(eslint): enable unicorn/no-chained-comparison - W-23094891`
19+
20+
## Skills to apply
21+
22+
- typescript — editing eslint.config.mjs (TypeScript config file)
23+
- packageJson — no pkg change, skip
24+
- verification — run lint
25+
- feature-branch — branch already created
26+
27+
## Verification
28+
29+
- `npm run lint` (or targeted `npx eslint 'packages/**/*.ts'`) passes — 0 new errors, 0 violations (confirmed pre-write via config, JSON output parsed for the rule id; NOT via `--rule` CLI flag which fails to load the plugin). The stop hook runs lint automatically; run manually only if it doesn't catch it.
30+
- Not e2e-covered (lint-only config change; no runtime behavior, no e2e impact).

.claude/plans/W-23094898.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# W-23094898 — Enable unicorn/no-double-comparison
2+
3+
## Context
4+
5+
- `unicorn/no-double-comparison` (error). Disallows 2 comparisons of same operands combinable into 1.
6+
- `eslint-plugin-unicorn@68.0.0`; rule file present `node_modules/eslint-plugin-unicorn/rules/no-double-comparison.js`.
7+
- 0 violations across 1447 linted files (added rule temporarily, ran `eslint . -f json`, filtered ruleId).
8+
- Pure config change. No source edits needed.
9+
- W-23094607 (unicorn v68 bump) — merged at 0b4b10ce5; branch current.
10+
11+
## Phases
12+
13+
### Phase 1 — enable rule
14+
15+
- `eslint.config.mjs`: add `'unicorn/no-double-comparison': 'error',` in alpha order between `no-array-sort` and `no-empty-file` (after line 171).
16+
- commit: `chore(lint): enable unicorn/no-double-comparison - W-23094898`
17+
18+
## Skills
19+
20+
- concise (plan/doc style)
21+
- typescript
22+
- verification
23+
24+
## Verification
25+
26+
- `npx eslint .` clean for the new rule (build `packages/eslint-local-rules` first via `npm run compile` if `out/index.js` missing). Use `NODE_OPTIONS=--max-old-space-size=8192` to avoid OOM on full-repo lint.
27+
- 0 violations; no follow-up fixes expected.
28+
- No e2e coverage applicable (lint-config only).

.claude/plans/W-23094920.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# W-23094920 — Enable unicorn/prefer-flat-math-min-max
2+
3+
## Context
4+
- Rule: flatten nested `Math.min`/`Math.max` (e.g. `Math.min(a, Math.min(b,c))``Math.min(a,b,c)`)
5+
- config-only. `eslint.config.mjs` unicorn block (alpha-ordered, insert after `prefer-export-from` L200 / before `prefer-includes` L202 — alpha slot for `prefer-flat-math-min-max`)
6+
- Grep `Math\.(min|max)\([^)]*Math\.(min|max)` over `packages/**/*.ts(x)` (excl `.min.js`): 0 hits → no source fixes needed
7+
- Real prerequisite = `eslint-plugin-unicorn@68` (rule absent <v68). v68 landed on `develop` via W-23094607 (commit `0b4b10ce5`). Worktree confirms: `node_modules/eslint-plugin-unicorn/package.json` = `68.0.0`, `node_modules/eslint-plugin-unicorn/rules/prefer-flat-math-min-max.js` present.
8+
- WI-stated dependency W-23094919 (`no-xor-as-exponentiation`) is a sibling lint-rule WI = **sequencing only**, NOT a functional prerequisite (this rule needs only v68, already on develop). As of 2026-06-23 W-23094919 Status__c = `Waiting`, NOT merged (`git log develop` has no W-23094919). Do not block on it — see Phase 0.
9+
10+
## Phases
11+
12+
### Phase 0 — dependency gate (blocking check)
13+
- Verify the real prereq (v68) is present:
14+
- `grep '"version"' node_modules/eslint-plugin-unicorn/package.json` → starts `68`
15+
- `ls node_modules/eslint-plugin-unicorn/rules/prefer-flat-math-min-max.js` exists
16+
- If either fails: `git fetch origin && git rebase origin/develop && npm install`, recheck. HALT if still absent.
17+
- W-23094919 sequencing check (informational, non-blocking):
18+
- `sf data query --query "SELECT Status__c FROM ADM_Work__c WHERE Name='W-23094919'" -o gus` and `git log develop --oneline | grep W-23094919`.
19+
- Rule does NOT depend on W-23094919's code (disjoint rules; only shared file is `eslint.config.mjs`). Proceed regardless of W-23094919 status. If strict sequencing required, HALT until W-23094919 is Closed/Completed AND on develop; otherwise continue.
20+
- Branch off current `develop` (W-23094919 is NOT on develop — do not branch off it).
21+
22+
### Phase 1 — enable rule
23+
- `eslint.config.mjs`: add `'unicorn/prefer-flat-math-min-max': 'error',` in unicorn block (alpha order: after `prefer-export-from`, before `prefer-includes`)
24+
- no code changes expected (0 violations)
25+
- commit subject: `chore(eslint): enable unicorn/prefer-flat-math-min-max - W-23094920`
26+
- commit body (repo convention — recent commits carry the trailer; config-only change has empty descriptive body, trailer only):
27+
28+
```
29+
chore(eslint): enable unicorn/prefer-flat-math-min-max - W-23094920
30+
31+
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
32+
```
33+
34+
If Phase 1 lint unexpectedly surfaces violations, the body must enumerate each touched source file (file-list pattern per sibling W-23094922).
35+
36+
## Skills
37+
- typescript
38+
- verification
39+
- feature-branch
40+
41+
## Verification
42+
- `npm run lint` clean (rule = error → zero violations; shard across packages — `npx eslint .` OOMs on the full tree)
43+
- `git log develop --oneline | grep W-23094919` re-checked at commit time (document branch-point reality)
44+
- no e2e coverage needed: lint-config-only change, zero source edits, no runtime behavior touched. (If Phase 1 surfaces source fixes, map each touched file to its owning package and run that package's existing desktop/headless specs as a regression guardrail; no NEW *.spec.ts warranted for a behavior-preserving flatten.)

.claude/plans/W-23094925.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# W-23094925 — enable unicorn/prefer-promise-with-resolvers
2+
3+
## Context
4+
- enable `unicorn/prefer-promise-with-resolvers` (error) in `eslint.config.mjs`
5+
- rule: prefer `Promise.withResolvers()` over extracting `resolve`/`reject` from `new Promise()` executor
6+
- depends: W-23094922 (must be merged first)
7+
- 2 violations across repo (verified by adding rule + lint run); rule meta `fixable: 'code'` but autofix declines both (executor has extra statements), so manual rewrite
8+
- prereq: build `packages/eslint-local-rules` (`npm run compile` there) before lint runs, else config import fails
9+
10+
## Phases
11+
12+
### Phase 1 — fix violations
13+
- `streamingClient.ts:137-191` `subscribe()`: replace `let subscribeAccept/subscribeReject` + `new Promise<void>(...)` (lines 138-143) with `const { promise: returnPromise, resolve: subscribeAccept, reject: subscribeReject } = Promise.withResolvers<void>();`. drop executor body. resolver usages (148,164,171) + `return returnPromise` (190) unchanged
14+
- `determineReporters.test.ts:159-165`: replace `let resolveInit` + `new Promise<void>(res => { resolveInit = res; })` with `const { promise, resolve: resolveInit } = Promise.withResolvers<void>();` returned from mock impl; `resolveInit()` at 169
15+
- files:
16+
- `packages/salesforcedx-apex-debugger/src/core/streamingClient.ts`
17+
- `packages/salesforcedx-utils-vscode/test/jest/telemetry/reporters/determineReporters.test.ts`
18+
- commit: `refactor: use Promise.withResolvers - W-23094925`
19+
20+
### Phase 2 — enable rule
21+
- add `'unicorn/prefer-promise-with-resolvers': 'error'` to unicorn block in `eslint.config.mjs` (alpha order, after `prefer-optional-catch-binding`)
22+
- files: `eslint.config.mjs`
23+
- commit: `build: enable unicorn/prefer-promise-with-resolvers - W-23094925`
24+
25+
## Skills
26+
- typescript
27+
- verification
28+
29+
## Verification
30+
- `npm run compile` in `packages/eslint-local-rules` (prereq for lint)
31+
- `npx eslint packages` — 0 `prefer-promise-with-resolvers` errors
32+
- typecheck/build `salesforcedx-apex-debugger``returnPromise` typed `Promise<void>`
33+
- jest `determineReporters.test.ts` "in-progress initialization" test passes (concurrent-init behavior)
34+
- streamingClient `subscribe()`: no direct unit test coverage (test file at `packages/salesforcedx-apex-debugger/test/unit/core/streamingClient.test.ts` only covers setReplayId/getReplayId + headers; subscribe() relies on integration/manual testing). run `npm test -w @salesforce/salesforcedx-apex-debugger` (package exposes `test`, not `test:unit`) — passes. refactor is mechanical/semantically identical; typecheck confirms `returnPromise` typed `Promise<void>`

.claude/plans/W-23094927.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# W-23094927 — enable unicorn/prefer-while-loop-condition
2+
3+
## Context
4+
- Enable `unicorn/prefer-while-loop-condition` (`error`) in `eslint.config.mjs`.
5+
- Rule: prefer condition in `while` over infinite loop + immediate `break`.
6+
- Independent of W-23094925 (PR #7556 DRAFT); branch from develop.
7+
- Unicorn v68 (`node_modules/eslint-plugin-unicorn`); rule fixable: `code`.
8+
- Verified zero violations: only `while(true)`/`for(;;)` matches sit in already-ignored paths:
9+
- `salesforcedx-visualforce-markup-language-server/src/**` (eslint.config.mjs:47)
10+
- `salesforcedx-aura-language-server/src/tern/**` (:48)
11+
- `salesforcedx-vscode-lightning/src/resources/**` (:51)
12+
- Pure config flip, no source edits.
13+
14+
## Phases
15+
16+
### Phase 1 — enable rule
17+
- `eslint.config.mjs`: add `'unicorn/prefer-while-loop-condition': 'error'` in unicorn block (after :219 `prefer-simple-condition-first`).
18+
- Files: `eslint.config.mjs`
19+
- Commit: `chore(eslint): enable unicorn/prefer-while-loop-condition - W-23094927`
20+
21+
## Skills
22+
- concise (plan/md)
23+
- packageJson (n/a — no manifest change)
24+
- typescript
25+
- verification
26+
27+
## Verification
28+
- `npx eslint --rule '{"unicorn/prefer-while-loop-condition":"error"}' "packages/**/*.{ts,js}"` → 0 violations (confirmed pre-write).
29+
- `npm run lint` clean.
30+
- Not e2e-covered (lint-config-only change).

0 commit comments

Comments
 (0)