Skip to content

feat(12.5): MFA polishing, UAT & E2E testing#131

Merged
FSM1 merged 17 commits into
mainfrom
feat/phase-12.5-mfa-polish-uat-e2e
Feb 16, 2026
Merged

feat(12.5): MFA polishing, UAT & E2E testing#131
FSM1 merged 17 commits into
mainfrom
feat/phase-12.5-mfa-polish-uat-e2e

Conversation

@FSM1

@FSM1 FSM1 commented Feb 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • Wire SecurityTab into SettingsPage with ARIA tab navigation (fixes ISSUE-004)
  • Add wallet E2E tests using @johanneskares/wallet-mock EIP-6963 mock provider (6 tests covering TC09-TC12)
  • Complete UAT for all 36 auth + MFA test cases (16 PASS / 19 SKIP / 1 NOTE, all 4 issues RESOLVED)

Test plan

  • Wallet E2E tests pass (pnpm --filter e2e test -- tests/wallet-login.spec.ts)
  • SecurityTab accessible via Settings > SECURITY tab
  • ARIA tab navigation works bidirectionally
  • VaultExport visible below tabs
  • MFA status badge shows [ENABLED] for enrolled user
  • Wallet connector list appears on [WALLET] click, cancel returns to initial state
  • Web app builds cleanly (pnpm --filter web build)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Settings page now has ARIA-compliant tabs exposing Security and Linked Methods; Vault export stays visible.
    • Login flow extended to support wallet connector interactions.
  • Bug Fixes

    • Security tab wiring completed; MFA flows unblocked and UAT-verified.
  • Tests

    • New wallet E2E suite with mock-wallet scenarios (happy path, cancel, no-wallet, error) and updated test helpers.
  • Style / Design

    • Wallet/MFA UI refinements, new warning color tokens, and updated linked-method visuals.
  • Documentation

    • Roadmap, state, planning, verification, and summaries updated; Phase 12.5 marked complete.

FSM1 and others added 10 commits February 16, 2026 21:13
Phase 12.5: MFA Polishing, UAT & E2E Testing
- 3 plan(s) in 2 wave(s)
- 2 parallel (wave 1), 1 sequential (wave 2)
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation

- Merge tabbed UI (LINKED METHODS | SECURITY) from orphaned Settings.tsx into SettingsPage.tsx
- Add SecurityTab import and render within security tab panel
- Preserve AppShell wrapper, auth guard, and VaultExport section below tabs
- Add full ARIA tablist/tab/tabpanel attributes for accessibility
- Delete orphaned Settings.tsx (no longer imported anywhere)

Resolves ISSUE-004

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Page

- Add @johanneskares/wallet-mock and viem as e2e devDependencies
- Extend LoginPage page object with wallet-specific selectors and methods
- New methods: clickWalletLogin, getWalletConnectors, selectWalletConnector,
  cancelWalletLogin, getWalletError, isWalletConnectorListVisible,
  getWalletStatus, isWalletLoginButtonVisible, isWalletLoginButtonEnabled,
  isNoWalletProvidersVisible

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 1/1
- Merge Settings.tsx tab UI into SettingsPage.tsx and remove orphan

SUMMARY: .planning/phases/12.5-mfa-polishing-uat-e2e/12.5-01-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…3 provider

- TC09: Happy path - mock wallet discovered via EIP-6963, SIWE flow initiated
- TC10: Cancel - connector list dismissed, button returns to idle
- TC11: No wallet - handles missing providers gracefully
- TC12: Error handling - intercepted nonce endpoint 500 shows error, allows retry
- Uses @johanneskares/wallet-mock with Hardhat account #0 for real ECDSA signatures
- Tests validate frontend UI flow independent of Core Kit availability

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- Install wallet-mock and add wallet selectors to LoginPage
- Create wallet-login.spec.ts with TC09-TC12 test cases

SUMMARY: .planning/phases/12.5-mfa-polishing-uat-e2e/12.5-02-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t cases

- ISSUE-004 marked RESOLVED (SecurityTab wired in Plan 01)
- TC09-TC12 updated to PASS (wallet E2E tests + interactive verification)
- TC27, TC30 updated to PASS (MFA enabled state verified interactively)
- TC25-26, TC28-29, TC31 updated from BLOCK to SKIP with reasons
- TC15-23 SKIP reasons clarified (multi-device/fresh account required)
- Session log updated with final UAT session results
- Summary: 16 PASS, 19 SKIP, 1 NOTE — all issues RESOLVED

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 3/3
- Task 1: Run wallet E2E tests (6/6 pass, no changes needed)
- Task 2: Interactive Playwright UAT verification (checkpoint approved)
- Task 3: Update UAT document with final statuses for all 36 test cases

Phase 12.5 complete: 16 PASS, 19 SKIP, 1 NOTE. All 4 issues RESOLVED.
SUMMARY: .planning/phases/12.5-mfa-polishing-uat-e2e/12.5-03-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@FSM1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 42 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Consolidates Settings UI into SettingsPage with ARIA tabs (exposing SecurityTab), deletes orphaned Settings.tsx, adds wallet E2E tests (TC09–TC12) with a mock wallet, extends LoginPage page-object for wallet flows, updates styles/design tokens, and marks Phase 12.5 complete in planning/UAT docs.

Changes

Cohort / File(s) Summary
Planning & UAT
.planning/ROADMAP.md, .planning/STATE.md, .planning/debug/corekit-auth-uat.md, .planning/phases/12.5-mfa-polishing-uat-e2e/*, .planning/quick/016-refine-wallet-and-mfa-ui-elements/*
Added Phase 12.5 plans, execution/verification docs, summaries, and UAT debug updates; marked Phase 12.5 complete and added quick UI refinement plan.
Settings UI Consolidation
Settings removal: apps/web/src/routes/Settings.tsx
SettingsPage update: apps/web/src/routes/SettingsPage.tsx
Deleted the orphaned Settings.tsx; merged ARIA-compliant tabbed UI into SettingsPage.tsx with SettingsTabId, keyboard handlers, and SecurityTab wired in; VaultExport retained below tabs.
E2E Wallet Tests & Page Object
tests/e2e/tests/wallet-login.spec.ts, tests/e2e/page-objects/login.page.ts, tests/e2e/package.json
Added Playwright wallet E2E suite (TC09–TC12) using @johanneskares/wallet-mock; extended LoginPage page object with wallet selectors/methods (click/select/cancel/status/error) and added devDependencies (wallet-mock, viem).
Frontend Styling & LinkedMethods
apps/web/src/App.css, apps/web/src/components/auth/LinkedMethods.tsx, designs/DESIGN.md
Added wallet-login/connector styles, MFA banner changed to warning/amber tokens, wallet icon changed from 'Ξ' to 'W', added "last method" hint in LinkedMethods, and updated design tokens/specs.
Quick UI Plan Summary Files
.planning/quick/016-refine-wallet-and-mfa-ui-elements/*
Added planning and summary docs describing CSS and LinkedMethods changes and verification steps.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Playwright Test
    participant Browser as Browser (App)
    participant Wallet as Mock Wallet (`@johanneskares/wallet-mock`)
    participant Backend as Server (SIWE nonce/verify)

    Test->>Browser: installMockWallet() then navigate to /login
    Browser->>Wallet: EIP-6963 discovery (request connectors)
    Wallet-->>Browser: announce connector (Mock Wallet)
    Test->>Browser: click wallet login button
    Browser->>Wallet: request connector list / show prompt
    Test->>Browser: select "Mock Wallet"
    Browser->>Wallet: request signature (SIWE message)
    Wallet-->>Browser: signature + address
    Browser->>Backend: POST /siwe/nonce -> POST /siwe/verify (verifyMessage)
    Backend-->>Browser: verification result (success/error)
    Browser-->>Test: redirect to /files OR show error/status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

render-preview

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(12.5): MFA polishing, UAT & E2E testing' clearly summarizes the main changes—completing Phase 12.5 work on MFA refinement, UAT completion, and E2E test integration. It is specific, concise, and directly reflects the core objectives.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/phase-12.5-mfa-polish-uat-e2e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.planning/STATE.md (1)

110-121: ⚠️ Potential issue | 🟡 Minor

Pending todo count mismatch: states 8 but lists 10 items.

Lines 112–121 contain 10 todo items, but line 110 says "8 pending todo(s)". This is a minor documentation inconsistency. Based on learnings, .planning/STATE.md metrics are auto-generated and minor arithmetic inconsistencies are acceptable, so flagging as informational only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/STATE.md around lines 110 - 121, Update the pending-todo count at
the top of .planning/STATE.md so the header "8 pending todo(s):" matches the
actual list of items (10); specifically change that header to "10 pending
todo(s):" or re-run the generator that produces the metric so it auto-syncs with
the listed filenames (e.g., `2026-02-07-web-worker-large-file-encryption.md`,
`2026-02-10-fix-flaky-post-reload-e2e-tests.md`, etc.).
🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In @.planning/STATE.md:
- Around line 110-121: Update the pending-todo count at the top of
.planning/STATE.md so the header "8 pending todo(s):" matches the actual list of
items (10); specifically change that header to "10 pending todo(s):" or re-run
the generator that produces the metric so it auto-syncs with the listed
filenames (e.g., `2026-02-07-web-worker-large-file-encryption.md`,
`2026-02-10-fix-flaky-post-reload-e2e-tests.md`, etc.).

In `@apps/web/src/routes/SettingsPage.tsx`:
- Around line 58-81: The tablist buttons (ids "tab-linked-methods" and
"tab-security" in SettingsPage.tsx) lack roving tabindex and keyboard handlers;
update the tab implementation so only the active tab has tabIndex={0} and
inactive tabs have tabIndex={-1}, add onKeyDown handler on the tab buttons to
handle ArrowLeft/ArrowRight to move focus (and optionally activate) between
tabs, and Home/End to jump to first/last tab, and use refs (e.g., an array of
tab refs) to call .focus() when moving; keep using activeTab and setActiveTab to
update state when arrows or Home/End activate a tab.
- Around line 84-100: The tab panels currently use both hidden={activeTab !==
'...'} and conditional rendering ({activeTab === '...' && <Component />}), which
is redundant; decide whether to preserve component state or unmount inactive
panels: to preserve state, keep the <LinkedMethods /> and <SecurityTab /> always
rendered inside their respective divs and remove the conditional expressions so
only hidden toggles visibility; to unmount inactive panels, keep the conditional
rendering and remove the hidden={...} attributes on the divs (update the divs
with id="panel-linked-methods" and id="panel-security" accordingly and rely
solely on activeTab checks).

In `@tests/e2e/package.json`:
- Around line 15-21: Update the viem dependency in tests/e2e package.json by
changing the version specifier for "viem" from "^2.44.4" to "^2.46.1"; open the
dependencies block where "viem" is declared (the dependencies array containing
"@johanneskares/wallet-mock", "@noble/ed25519", "@playwright/test",
"@types/node", "dotenv", "typescript", "viem") and modify the "viem" entry, then
run npm install (or yarn) to refresh lockfile and verify tests still pass.

In `@tests/e2e/page-objects/login.page.ts`:
- Around line 150-152: The cancelWalletLogin method clicks the cancel button
without ensuring it's visible, causing flakiness; update async
cancelWalletLogin() to wait for this.walletConnectorCancel.waitFor({ state:
'visible' }) (mirroring clickWalletLogin) before calling
this.walletConnectorCancel.click() so the element is present and visible prior
to interaction.
- Around line 36-59: The wallet selectors are inconsistent and fragile:
walletLoginButton uses data-testid but walletConnectorList,
walletConnectorCancel, walletNoProviders, walletLoginStatus, and walletError use
CSS class selectors; change those to stable data-testid selectors (e.g., replace
'.wallet-connector-list' etc. with matching [data-testid="..."] values), update
the page object getters (walletConnectorList, walletConnectorCancel,
walletNoProviders, walletLoginStatus, walletError) to use those data-testid
attributes, and make sure the app markup provides the corresponding data-testid
attributes so the tests remain stable against styling changes.

In `@tests/e2e/tests/wallet-login.spec.ts`:
- Around line 22-23: Test private key HARDHAT_ACCOUNT_0_KEY is a known Hardhat
test key and should be whitelisted for Gitleaks; to fix, add an inline
suppression comment on the constant (e.g., append "# gitleaks:allow" to the
declaration of HARDHAT_ACCOUNT_0_KEY) or add a .gitleaksignore entry referencing
this file or the exact secret string so the scanner treats it as an allowed
false positive, ensuring the symbol HARDHAT_ACCOUNT_0_KEY remains unchanged and
CI no longer flags it.
- Around line 63-68: The test is using immediate, non-retrying assertions after
UI interactions which can flake; update calls that check the connector list
visibility and contents (e.g., after clickWalletLogin()) to use Playwright's
auto-retrying locator assertions or toPass wrappers. Concretely, expose and use
the page-object locator (walletConnectorListLocator) with await
expect(locator).toBeVisible() before calling
isWalletConnectorListVisible()/getWalletConnectors(), or wrap the existing
isWalletConnectorListVisible() and getWalletConnectors() checks in await
expect(async () => { ... }).toPass({ timeout: 5000 }); apply the same change to
the cancel-flow assertions that use
isWalletConnectorListVisible()/getWalletConnectors() so all visibility/content
checks retry until stable.
- Around line 264-266: Replace the raw locator in the test with a locator
exposed by the LoginPage to preserve page-object encapsulation: add a
locator-returning member (e.g., a getter named walletErrorLocator) to the
LoginPage class that returns the same locator used by
page.locator('.wallet-login-wrapper .login-error'), then update the test to call
loginPage.walletErrorLocator and pass that to expect(...).toBeVisible({ timeout:
10000 }) instead of using page.locator(...) directly; keep the existing
getWalletError() method for retrieving text if still needed.
- Around line 96-114: The third branch in the Promise.race (the
page.waitForTimeout(5000) branch that checks
loginPage.getWalletStatus()/getWalletError()) can short-circuit a slower but
valid redirect from page.waitForURL('**/files', { timeout: 20000 }), so remove
that fixed 5s timeout branch and replace it with a short-poll loop that
repeatedly calls loginPage.getWalletError() and loginPage.getWalletStatus()
(e.g., every 250–500ms) until either page.waitForURL resolves or the 20s overall
timeout elapses; update the Promise.race to only race page.waitForURL(...) and
the polling/error-check promise so slow-but-successful redirects are not masked.
🧹 Nitpick comments (6)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed.


In `@apps/web/src/routes/SettingsPage.tsx`:
- Around line 84-100: The tab panels currently use both hidden={activeTab !==
'...'} and conditional rendering ({activeTab === '...' && <Component />}), which
is redundant; decide whether to preserve component state or unmount inactive
panels: to preserve state, keep the <LinkedMethods /> and <SecurityTab /> always
rendered inside their respective divs and remove the conditional expressions so
only hidden toggles visibility; to unmount inactive panels, keep the conditional
rendering and remove the hidden={...} attributes on the divs (update the divs
with id="panel-linked-methods" and id="panel-security" accordingly and rely
solely on activeTab checks).

In `@tests/e2e/page-objects/login.page.ts`:
- Around line 36-59: The wallet selectors are inconsistent and fragile:
walletLoginButton uses data-testid but walletConnectorList,
walletConnectorCancel, walletNoProviders, walletLoginStatus, and walletError use
CSS class selectors; change those to stable data-testid selectors (e.g., replace
'.wallet-connector-list' etc. with matching [data-testid="..."] values), update
the page object getters (walletConnectorList, walletConnectorCancel,
walletNoProviders, walletLoginStatus, walletError) to use those data-testid
attributes, and make sure the app markup provides the corresponding data-testid
attributes so the tests remain stable against styling changes.

In `@tests/e2e/tests/wallet-login.spec.ts`:
- Around line 22-23: Test private key HARDHAT_ACCOUNT_0_KEY is a known Hardhat
test key and should be whitelisted for Gitleaks; to fix, add an inline
suppression comment on the constant (e.g., append "# gitleaks:allow" to the
declaration of HARDHAT_ACCOUNT_0_KEY) or add a .gitleaksignore entry referencing
this file or the exact secret string so the scanner treats it as an allowed
false positive, ensuring the symbol HARDHAT_ACCOUNT_0_KEY remains unchanged and
CI no longer flags it.
- Around line 63-68: The test is using immediate, non-retrying assertions after
UI interactions which can flake; update calls that check the connector list
visibility and contents (e.g., after clickWalletLogin()) to use Playwright's
auto-retrying locator assertions or toPass wrappers. Concretely, expose and use
the page-object locator (walletConnectorListLocator) with await
expect(locator).toBeVisible() before calling
isWalletConnectorListVisible()/getWalletConnectors(), or wrap the existing
isWalletConnectorListVisible() and getWalletConnectors() checks in await
expect(async () => { ... }).toPass({ timeout: 5000 }); apply the same change to
the cancel-flow assertions that use
isWalletConnectorListVisible()/getWalletConnectors() so all visibility/content
checks retry until stable.
- Around line 264-266: Replace the raw locator in the test with a locator
exposed by the LoginPage to preserve page-object encapsulation: add a
locator-returning member (e.g., a getter named walletErrorLocator) to the
LoginPage class that returns the same locator used by
page.locator('.wallet-login-wrapper .login-error'), then update the test to call
loginPage.walletErrorLocator and pass that to expect(...).toBeVisible({ timeout:
10000 }) instead of using page.locator(...) directly; keep the existing
getWalletError() method for retrieving text if still needed.
- Around line 96-114: The third branch in the Promise.race (the
page.waitForTimeout(5000) branch that checks
loginPage.getWalletStatus()/getWalletError()) can short-circuit a slower but
valid redirect from page.waitForURL('**/files', { timeout: 20000 }), so remove
that fixed 5s timeout branch and replace it with a short-poll loop that
repeatedly calls loginPage.getWalletError() and loginPage.getWalletStatus()
(e.g., every 250–500ms) until either page.waitForURL resolves or the 20s overall
timeout elapses; update the Promise.race to only race page.waitForURL(...) and
the polling/error-check promise so slow-but-successful redirects are not masked.
apps/web/src/routes/SettingsPage.tsx (1)

84-100: Redundant hidden attribute alongside conditional rendering.

Each panel uses both hidden={activeTab !== '...'} and {activeTab === '...' && <Component />}. Either approach alone prevents the content from being visible/accessible. Using both means the DOM node is present (with hidden) but the children are unmounted — the hidden attribute is effectively unused.

Pick one strategy. If you want to preserve state across tab switches (e.g., form inputs), remove conditional rendering and rely on hidden alone. If you prefer unmounting inactive panels, remove hidden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/SettingsPage.tsx` around lines 84 - 100, The tab panels
currently use both hidden={activeTab !== '...'} and conditional rendering
({activeTab === '...' && <Component />}), which is redundant; decide whether to
preserve component state or unmount inactive panels: to preserve state, keep the
<LinkedMethods /> and <SecurityTab /> always rendered inside their respective
divs and remove the conditional expressions so only hidden toggles visibility;
to unmount inactive panels, keep the conditional rendering and remove the
hidden={...} attributes on the divs (update the divs with
id="panel-linked-methods" and id="panel-security" accordingly and rely solely on
activeTab checks).
tests/e2e/page-objects/login.page.ts (1)

36-59: Selector strategy is inconsistent: data-testid for wallet button, CSS classes for the rest.

walletLoginButton uses [data-testid="wallet-login-button"] while all other wallet selectors rely on CSS class names (.wallet-connector-list, .wallet-connector-cancel, etc.). CSS class selectors are more brittle—they break on styling refactors. Consider using data-testid attributes consistently for all wallet UI elements.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/page-objects/login.page.ts` around lines 36 - 59, The wallet
selectors are inconsistent and fragile: walletLoginButton uses data-testid but
walletConnectorList, walletConnectorCancel, walletNoProviders,
walletLoginStatus, and walletError use CSS class selectors; change those to
stable data-testid selectors (e.g., replace '.wallet-connector-list' etc. with
matching [data-testid="..."] values), update the page object getters
(walletConnectorList, walletConnectorCancel, walletNoProviders,
walletLoginStatus, walletError) to use those data-testid attributes, and make
sure the app markup provides the corresponding data-testid attributes so the
tests remain stable against styling changes.
tests/e2e/tests/wallet-login.spec.ts (4)

22-23: Gitleaks false positive — Hardhat account #0 is a well-known test key.

This is the default Hardhat/Anvil account #0 private key, publicly documented and deterministic. It holds no real funds and is standard practice for E2E testing. Consider adding an inline # gitleaks:allow comment or a .gitleaksignore entry to suppress future CI noise.

Suppress Gitleaks alert
 // Hardhat account `#0` private key (well-known, deterministic, no real funds)
 const HARDHAT_ACCOUNT_0_KEY =
-  '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' as const;
+  '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' as const; // gitleaks:allow -- Hardhat account `#0`, public test key
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/tests/wallet-login.spec.ts` around lines 22 - 23, Test private key
HARDHAT_ACCOUNT_0_KEY is a known Hardhat test key and should be whitelisted for
Gitleaks; to fix, add an inline suppression comment on the constant (e.g.,
append "# gitleaks:allow" to the declaration of HARDHAT_ACCOUNT_0_KEY) or add a
.gitleaksignore entry referencing this file or the exact secret string so the
scanner treats it as an allowed false positive, ensuring the symbol
HARDHAT_ACCOUNT_0_KEY remains unchanged and CI no longer flags it.

63-68: Bare assertions after clickWalletLogin() may be flaky.

Lines 64 and 166–176 perform immediate expect(…).toBe(true/false) checks right after UI interactions (click, cancel) without any retry/polling. If the connector list renders asynchronously or an animation is involved, these will intermittently fail. Wrap them in expect(…).toPass() or use Playwright's auto-retrying expect(locator).toBeVisible() instead.

Example: use auto-retrying locator assertions

For instance, instead of:

-    expect(await loginPage.isWalletConnectorListVisible()).toBe(true);

Expose the underlying locator from the page object and use:

await expect(loginPage.walletConnectorListLocator).toBeVisible();

Or at minimum wrap in toPass:

await expect(async () => {
  expect(await loginPage.isWalletConnectorListVisible()).toBe(true);
}).toPass({ timeout: 5000 });

The same applies to the cancel-flow assertions on lines 172–176 and line 219.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/tests/wallet-login.spec.ts` around lines 63 - 68, The test is using
immediate, non-retrying assertions after UI interactions which can flake; update
calls that check the connector list visibility and contents (e.g., after
clickWalletLogin()) to use Playwright's auto-retrying locator assertions or
toPass wrappers. Concretely, expose and use the page-object locator
(walletConnectorListLocator) with await expect(locator).toBeVisible() before
calling isWalletConnectorListVisible()/getWalletConnectors(), or wrap the
existing isWalletConnectorListVisible() and getWalletConnectors() checks in
await expect(async () => { ... }).toPass({ timeout: 5000 }); apply the same
change to the cancel-flow assertions that use
isWalletConnectorListVisible()/getWalletConnectors() so all visibility/content
checks retry until stable.

264-266: Raw locator breaks page-object encapsulation.

page.locator('.wallet-login-wrapper .login-error') duplicates knowledge of the DOM structure that should live in LoginPage. The getWalletError() method (used on line 269) already abstracts this. Consider adding a locator-returning method to LoginPage so this line can use auto-retrying expect(locator).toBeVisible() via the page object.

Proposed approach

In LoginPage, expose:

get walletErrorLocator() {
  return this.page.locator('.wallet-login-wrapper .login-error');
}

Then in the test:

-    const errorLocator = page.locator('.wallet-login-wrapper .login-error');
-    await expect(errorLocator).toBeVisible({ timeout: 10000 });
+    await expect(loginPage.walletErrorLocator).toBeVisible({ timeout: 10000 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/tests/wallet-login.spec.ts` around lines 264 - 266, Replace the raw
locator in the test with a locator exposed by the LoginPage to preserve
page-object encapsulation: add a locator-returning member (e.g., a getter named
walletErrorLocator) to the LoginPage class that returns the same locator used by
page.locator('.wallet-login-wrapper .login-error'), then update the test to call
loginPage.walletErrorLocator and pass that to expect(...).toBeVisible({ timeout:
10000 }) instead of using page.locator(...) directly; keep the existing
getWalletError() method for retrieving text if still needed.

96-114: Promise.race with waitForTimeout(5000) may short-circuit valid flows.

The third branch resolves after a fixed 5 s wait. If the redirect (branch 1) takes between 5–20 s, the timeout branch wins the race and the test reports "in-progress" or "timeout" instead of "redirected". Since the test body already accepts non-redirect outcomes gracefully, this won't fail — but it may mask real successes in environments where the backend is available but slow.

Consider increasing the fixed wait or removing the third branch entirely and relying on the 20 s waitForURL timeout plus a shorter getWalletError poll to cover both outcomes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/tests/wallet-login.spec.ts` around lines 96 - 114, The third branch
in the Promise.race (the page.waitForTimeout(5000) branch that checks
loginPage.getWalletStatus()/getWalletError()) can short-circuit a slower but
valid redirect from page.waitForURL('**/files', { timeout: 20000 }), so remove
that fixed 5s timeout branch and replace it with a short-poll loop that
repeatedly calls loginPage.getWalletError() and loginPage.getWalletStatus()
(e.g., every 250–500ms) until either page.waitForURL resolves or the 20s overall
timeout elapses; update the Promise.race to only race page.waitForURL(...) and
the polling/error-check promise so slow-but-successful redirects are not masked.

Comment thread apps/web/src/routes/SettingsPage.tsx
Comment thread tests/e2e/package.json Outdated
Comment thread tests/e2e/page-objects/login.page.ts
FSM1 and others added 5 commits February 16, 2026 22:17
- Add 19 wallet login CSS classes matching terminal aesthetic
- Change MFA enrollment banner to amber accent (rgb(245 158 11))
- Update SecurityTab enable-btn to font-size-sm with larger padding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dMethods

- Change wallet icon from Greek Xi to "W" for universal legibility
- Add visible "// last method" hint next to disabled unlink button
- Add CSS for linked-methods-unlink-hint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2 (+ 1 pending human verification)
- Add WalletLoginButton CSS and fix MFA banner styling
- Fix wallet icon and unlink disabled state in LinkedMethods

SUMMARY: .planning/quick/016-refine-wallet-and-mfa-ui-elements/016-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Quick task completed — wallet login fully styled, MFA banner amber accent,
security enable button sized up, wallet icon and unlink hint improved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update Pencil frames to match CSS implementation (5 discrepancies fixed)
- Add warning/amber color tokens, text-dim, error colors to DESIGN.md
- Add Wallet Button, MFA Banner, Connector Picker, Linked Method patterns
- Add 4 design decisions from Phase 12.5 refinement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In `@designs/DESIGN.md`:
- Around line 13-25: The table defines two different hex values for the same CSS
variable (--color-text-muted) which is ambiguous; pick which hex should remain
as --color-text-muted and rename the other to a distinct variable (e.g.,
--color-text-dim, --color-label-muted or --color-text-muted-variant) and update
its Usage entry accordingly so each hex maps to a unique CSS variable; ensure
references to --color-text-muted, `#006644`, and `#8b9a8f` in the DESIGN.md table
are adjusted to reflect the chosen mapping and new variable name.

Comment thread designs/DESIGN.md
Comment on lines +13 to +25
| Hex Code | Opacity | Usage | CSS Variable |
| --------- | ------- | -------------------------- | ---------------------- |
| `#000000` | 100% | Background, surfaces | `--color-background` |
| `#00D084` | 100% | Primary accent, borders | `--color-primary` |
| `#00D084` | 40% | Glow effects | `--color-glow` |
| `#006644` | 100% | Secondary text, muted | `--color-text-muted` |
| `#003322` | 100% | Row separators, dividers | `--color-border-muted` |
| `#F59E0B` | 100% | Warning accent, MFA banner | `--color-warning` |
| `#F59E0B` | 8% | Warning background tint | — |
| `#F59E0B` | 25% | Warning border | — |
| `#EF4444` | 100% | Error, danger actions | `--color-error` |
| `#4a5a4e` | 100% | Dim text, dismiss buttons | `--color-text-dim` |
| `#8b9a8f` | 100% | Muted labels | `--color-text-muted` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate --color-text-muted mapping with different hex values.

Line 18 maps #006644 to --color-text-muted ("Secondary text, muted") and Line 25 maps #8b9a8f to the same --color-text-muted ("Muted labels"). Two different hex values for the same CSS variable name will cause confusion when referencing this design system doc for implementations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@designs/DESIGN.md` around lines 13 - 25, The table defines two different hex
values for the same CSS variable (--color-text-muted) which is ambiguous; pick
which hex should remain as --color-text-muted and rename the other to a distinct
variable (e.g., --color-text-dim, --color-label-muted or
--color-text-muted-variant) and update its Usage entry accordingly so each hex
maps to a unique CSS variable; ensure references to --color-text-muted, `#006644`,
and `#8b9a8f` in the DESIGN.md table are adjusted to reflect the chosen mapping
and new variable name.

- Add roving tabindex and arrow key navigation to settings tabs (WAI-ARIA)
- Update viem to ^2.46.1 in e2e devDependencies
- Add waitFor visibility check before click in cancelWalletLogin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In `@tests/e2e/page-objects/login.page.ts`:
- Around line 126-136: The getWalletConnectors function loops over options with
count/nth/textContent causing sequential awaits; replace that logic by using
Playwright's options.allTextContents() to fetch all texts at once, then
map/clean each string (remove surrounding brackets and trim) before returning;
update references to walletConnectorList.waitFor(...) and the options locator
('.wallet-connector-option') within getWalletConnectors to use allTextContents()
and perform the same regex replace on each item.
- Around line 36-59: Replace the brittle CSS-class locators with stable
data-testid locators: update the component to add data-testid attributes for the
wallet elements and then change the page-object getters walletConnectorList,
walletConnectorCancel, walletNoProviders, walletLoginStatus, walletError to use
this.page.locator('[data-testid="..."]') instead of class selectors; also add a
new getter walletConnectorOption (e.g.,
this.page.locator('[data-testid="wallet-connector-option"]')) and update the
inline usages of '.wallet-connector-option' to use this.walletConnectorOption so
all wallet selectors consistently rely on data-testid.
🧹 Nitpick comments (2)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed.


In `@tests/e2e/page-objects/login.page.ts`:
- Around line 126-136: The getWalletConnectors function loops over options with
count/nth/textContent causing sequential awaits; replace that logic by using
Playwright's options.allTextContents() to fetch all texts at once, then
map/clean each string (remove surrounding brackets and trim) before returning;
update references to walletConnectorList.waitFor(...) and the options locator
('.wallet-connector-option') within getWalletConnectors to use allTextContents()
and perform the same regex replace on each item.
- Around line 36-59: Replace the brittle CSS-class locators with stable
data-testid locators: update the component to add data-testid attributes for the
wallet elements and then change the page-object getters walletConnectorList,
walletConnectorCancel, walletNoProviders, walletLoginStatus, walletError to use
this.page.locator('[data-testid="..."]') instead of class selectors; also add a
new getter walletConnectorOption (e.g.,
this.page.locator('[data-testid="wallet-connector-option"]')) and update the
inline usages of '.wallet-connector-option' to use this.walletConnectorOption so
all wallet selectors consistently rely on data-testid.
tests/e2e/page-objects/login.page.ts (2)

126-136: getWalletConnectors can be simplified with allTextContents().

The manual loop over count / nth(i) / textContent() can be replaced with Playwright's built-in allTextContents(), which returns all matching elements' text in one call. This also avoids the implicit sequential await per element.

Simplified implementation
  async getWalletConnectors(): Promise<string[]> {
    await this.walletConnectorList.waitFor({ state: 'visible', timeout: 5000 });
-   const options = this.page.locator('.wallet-connector-option');
-   const count = await options.count();
-   const names: string[] = [];
-   for (let i = 0; i < count; i++) {
-     const text = await options.nth(i).textContent();
-     if (text) names.push(text.replace(/^\[|\]$/g, '').trim());
-   }
-   return names;
+   const texts = await this.page.locator('.wallet-connector-option').allTextContents();
+   return texts
+     .map(t => t.replace(/^\[|\]$/g, '').trim())
+     .filter(Boolean);
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/page-objects/login.page.ts` around lines 126 - 136, The
getWalletConnectors function loops over options with count/nth/textContent
causing sequential awaits; replace that logic by using Playwright's
options.allTextContents() to fetch all texts at once, then map/clean each string
(remove surrounding brackets and trim) before returning; update references to
walletConnectorList.waitFor(...) and the options locator
('.wallet-connector-option') within getWalletConnectors to use allTextContents()
and perform the same regex replace on each item.

36-59: Inconsistent selector strategy: CSS classes vs. data-testid attributes.

The existing email/Google selectors and walletLoginButton all use [data-testid="..."], but the remaining wallet selectors rely on plain CSS classes (.wallet-connector-list, .wallet-connector-cancel, etc.). CSS classes are styling concerns and can be renamed or removed during refactors without breaking production code — but they will silently break these tests. Prefer data-testid attributes for all test-facing selectors to keep the test suite resilient.

Also, .wallet-connector-option is used inline at line 128 and line 143 but isn't declared as a getter alongside the other selectors, further splitting the locator definitions.

Suggested approach — add data-testid attributes in the component and update selectors here
  private get walletConnectorList() {
-   return this.page.locator('.wallet-connector-list');
+   return this.page.locator('[data-testid="wallet-connector-list"]');
  }

  private get walletConnectorCancel() {
-   return this.page.locator('.wallet-connector-cancel');
+   return this.page.locator('[data-testid="wallet-connector-cancel"]');
  }

  private get walletNoProviders() {
-   return this.page.locator('.wallet-no-providers');
+   return this.page.locator('[data-testid="wallet-no-providers"]');
  }

  private get walletLoginStatus() {
-   return this.page.locator('.wallet-login-status');
+   return this.page.locator('[data-testid="wallet-login-status"]');
  }

  private get walletError() {
-   return this.page.locator('.wallet-login-wrapper .login-error');
+   return this.page.locator('[data-testid="wallet-login-error"]');
  }
+
+  private get walletConnectorOption() {
+    return this.page.locator('[data-testid="wallet-connector-option"]');
+  }

Then use this.walletConnectorOption at lines 128 and 143 instead of the inline locator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/page-objects/login.page.ts` around lines 36 - 59, Replace the
brittle CSS-class locators with stable data-testid locators: update the
component to add data-testid attributes for the wallet elements and then change
the page-object getters walletConnectorList, walletConnectorCancel,
walletNoProviders, walletLoginStatus, walletError to use
this.page.locator('[data-testid="..."]') instead of class selectors; also add a
new getter walletConnectorOption (e.g.,
this.page.locator('[data-testid="wallet-connector-option"]')) and update the
inline usages of '.wallet-connector-option' to use this.walletConnectorOption so
all wallet selectors consistently rely on data-testid.

The field was renamed in Phase 12.3 but the E2E helper was not updated,
leaving vaultKeypair null and causing folder creation to throw before
closing the dialog. Also close create-folder dialog in finally block
so it always dismisses even on error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FSM1 FSM1 merged commit 7bd4067 into main Feb 16, 2026
7 checks passed
@FSM1 FSM1 deleted the feat/phase-12.5-mfa-polish-uat-e2e branch February 21, 2026 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant