Skip to content

Add CI test job and global error boundary for webview-app#1957

Open
Tranquil-Flow wants to merge 6 commits into
devfrom
chore/webview-launch-readiness
Open

Add CI test job and global error boundary for webview-app#1957
Tranquil-Flow wants to merge 6 commits into
devfrom
chore/webview-launch-readiness

Conversation

@Tranquil-Flow

@Tranquil-Flow Tranquil-Flow commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add test job to webview-app-ci.yml that runs webview-app (151 tests) and webview-bridge (68 tests) on every PR and push
  • Fix pre-existing test failures: vi.mock hoisting in settingsScreens, missing euclid mock exports in recoverySupportScreens
  • Add global React error boundary — catches unhandled render errors and shows recovery UI instead of white screen, with "Try again" and "Close" (fires lifecycle.dismiss to host)

Closes SELF-2436, SELF-2440

Test plan

  • yarn workspace @selfxyz/webview-app test — 151/151 pass
  • yarn workspace @selfxyz/webview-bridge test — 68/68 pass
  • yarn fmt and yarn lint clean
  • yarn build succeeds
  • CI test job runs successfully on this PR
  • Error boundary catches thrown errors (verified via test)
  • "Close" button fires lifecycle.dismiss to host (verified via test)

Summary by CodeRabbit

  • New Features

    • Added a runtime error boundary with a full-viewport fallback offering “Try again” and “Close” actions.
  • Bug Fixes

    • Closing tunnel/result screens now records a cancellation result and dismisses the view; receipt UI and close/confirm behaviors adjusted.
  • Tests

    • Added and updated tests covering the error boundary, recovery flows, tunnel close behavior, and improved test mocks.
  • Chores

    • Added a CI job to build workspaces and run unit tests.

…lures

The CI workflow ran build, lint, format, and type-check but never executed tests. Add a test job that runs both webview-app (147 tests) and webview-bridge (68 tests) on every PR and push. Fix two pre-existing test file failures: vi.mock hoisting issue in settingsScreens and missing euclid mock exports in recoverySupportScreens.
@vercel

vercel Bot commented Apr 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
self-webview-app Ready Ready Preview, Comment Apr 20, 2026 11:10am

Request Review

@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3a8f70bb-d53a-44c5-a19f-5cdfe4f94c61

📥 Commits

Reviewing files that changed from the base of the PR and between ab9d1c1 and aa0704e.

📒 Files selected for processing (1)
  • packages/webview-app/tests/screens/tunnel/tunnelFlowScreens.test.tsx

📝 Walkthrough

Walkthrough

Adds an ErrorBoundary component and wraps the app to capture render-time errors; updates multiple tests and mocks to reflect changed behavior; modifies tunnel flow tests to assert async lifecycle/analytics side effects on close; and adds a CI test job to build workspaces and run workspace tests.

Changes

Cohort / File(s) Summary
CI Configuration
.github/workflows/webview-app-ci.yml
Added a new test job (ubuntu-latest, 15m timeout) that checks out code, installs dependencies, builds workspaces (@selfxyz/common, @selfxyz/mobile-sdk-alpha, @selfxyz/webview-bridge), and runs tests for @selfxyz/webview-app and @selfxyz/webview-bridge.
Error Boundary Implementation
packages/webview-app/src/components/ErrorBoundary.tsx, packages/webview-app/src/main.tsx
Added ErrorBoundary class component (captures render errors, logs, shows full-viewport fallback with “Try again” and “Close”); wrapped App with ErrorBoundary. Close calls lifecycle.dismiss({ reason: 'user_cancel' }).
ErrorBoundary Tests
packages/webview-app/tests/components/ErrorBoundary.test.tsx
Added Vitest/RTL tests covering normal render, fallback UI on throw, recovery via “Try again”, and dismiss via “Close” (mocks BridgeProvider and lifecycle.dismiss).
Test helper / mocks adjustments
packages/webview-app/tests/screens/account/settingsScreens.test.tsx, packages/webview-app/tests/screens/recovery/recoverySupportScreens.test.tsx
Hoisted mockDocumentStore with vi.hoisted(), changed location assertion to use getAllByTestId('location') and assert last element; updated recovery mocks to include borderRadius.mdd and a RecoveryPhrase mock; removed intermediate /recovery/success step and related interactions in some recovery tests.
Tunnel flow tests (async lifecycle/analytics)
packages/webview-app/tests/screens/tunnel/tunnelFlowScreens.test.tsx
Reworked close-action expectations to assert async side effects: lifecycle.setResult with failure payload, analytics.trackEvent('tunnel_result_cancelled', { source }), then lifecycle.dismiss(); default missing source to 'proving'; adjusted receipt UI expectations and added waitFor/async handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding a CI test job and a global error boundary for webview-app.
Description check ✅ Passed The description follows the template structure with both Summary and Test plan sections filled out comprehensively, covering the key changes and verification steps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/webview-launch-readiness

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.

Wrap the app in a React error boundary that catches unhandled render errors and shows a recovery UI instead of a white screen. The fallback screen offers "Try again" (resets error state) and "Close" (fires lifecycle dismiss to the host app). Placed between BridgeProvider and App so it has bridge access for host notification. Includes 4 tests covering render, catch, retry, and dismiss flows.
@Tranquil-Flow Tranquil-Flow changed the title Add test job to webview-app CI and fix test failures Add CI test job and global error boundary for webview-app Apr 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 94d4884d-5481-4829-ba9f-efb05f8dadba

📥 Commits

Reviewing files that changed from the base of the PR and between b9c5b4a and b5e649d.

📒 Files selected for processing (6)
  • .github/workflows/webview-app-ci.yml
  • packages/webview-app/src/components/ErrorBoundary.tsx
  • packages/webview-app/src/main.tsx
  • packages/webview-app/tests/components/ErrorBoundary.test.tsx
  • packages/webview-app/tests/screens/account/settingsScreens.test.tsx
  • packages/webview-app/tests/screens/recovery/recoverySupportScreens.test.tsx

Comment on lines +68 to +85
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build common
run: yarn workspace @selfxyz/common build
- name: Build mobile-sdk-alpha
run: yarn workspace @selfxyz/mobile-sdk-alpha build
- name: Build webview-bridge
run: yarn workspace @selfxyz/webview-bridge build
- name: Run tests
run: yarn workspace @selfxyz/webview-app test
- name: Run webview-bridge tests
run: yarn workspace @selfxyz/webview-bridge test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing required CI fast-fail guard before test execution

Line 68 onward adds the new test job, but it does not include the required pre-test validation step to detect nested require() patterns. This can reintroduce avoidable OOM-style CI failures later in the job.

As per coding guidelines: ".github/workflows/*.yml: Add a CI fast-fail check step that runs the validation script to detect nested require() patterns before test execution to prevent out-of-memory pipeline failures."

Comment thread packages/webview-app/src/components/ErrorBoundary.tsx
Comment on lines +33 to +38
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'error').mockImplementation(() => {});
});

afterEach(cleanup);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Restore console.error spy after each test to avoid cross-test leakage

Line 35 installs a global spy, but Line 38 does not restore it. This can leak mocked console behavior into subsequent tests.

Suggested fix
-  afterEach(cleanup);
+  afterEach(() => {
+    cleanup();
+    vi.restoreAllMocks();
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(cleanup);
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});

Wrap componentDidCatch logging in a DEV check so production builds only emit a generic message. Prevents raw error objects and component stacks from surfacing in host app logs where they could expose internal state.
@Tranquil-Flow

Copy link
Copy Markdown
Contributor Author

@greptileai review

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds CI test coverage for the webview-app and webview-bridge packages, introduces a global React error boundary that surfaces a recovery UI instead of a white screen, and fixes three pre-existing test failures that were blocking CI.

  • CI (webview-app-ci.yml): New test job mirrors the structure of the existing build/lint/types jobs — builds the dependency chain, then runs both @selfxyz/webview-app and @selfxyz/webview-bridge test suites.
  • ErrorBoundary.tsx: Class/function wrapper split is the correct React pattern for hooking useBridge() into a class component boundary. Placement in main.tsx (inside BridgeProvider) ensures the hook is always satisfied. lifecycle.dismiss({ reason: 'user_cancel' }) on close is the right bridge call. The only issue is that the styles object uses raw hex values throughout rather than euclid design tokens (see inline comment).
  • settingsScreens.test.tsx: vi.hoisted() wrapping for mockDocumentStore is the correct fix for Vitest's mock hoisting semantics. The expectLocation change to getAllByTestId(...).at(-1) correctly handles nested route test-ids.
  • recoverySupportScreens.test.tsx: Added missing borderRadius.mdd and RecoveryPhrase mock exports. Removal of the intermediate /recovery/success assertions aligns tests with the actual implementation.

Confidence Score: 4/5

Safe to merge — one non-blocking style rule violation remains (raw hex in ErrorBoundary styles).

The CI job, error boundary logic, bridge integration, and all three test fixes are correct. The only finding is raw hex color values in the ErrorBoundary styles object instead of euclid design tokens, which violates project style rules but has no runtime impact.

packages/webview-app/src/components/ErrorBoundary.tsx — styles object should use euclid tokens instead of raw hex values.

Important Files Changed

Filename Overview
.github/workflows/webview-app-ci.yml New test job added that builds the dependency chain then runs both webview-app and webview-bridge test suites. Consistent with existing jobs; no issues found.
packages/webview-app/src/components/ErrorBoundary.tsx New global error boundary using a class/function wrapper split. Logic and bridge integration are sound; styles object uses raw hex values instead of euclid design tokens.
packages/webview-app/src/main.tsx Wraps App in ErrorBoundary inside BridgeProvider; placement is correct so useBridge() is always satisfied.
packages/webview-app/tests/components/ErrorBoundary.test.tsx Four focused tests cover the healthy path, fallback render, retry recovery, and dismiss bridge call. Dynamic await import() after mocks is correct Vitest pattern.
packages/webview-app/tests/screens/account/settingsScreens.test.tsx Fixes vi.mock hoisting issue by wrapping mockDocumentStore in vi.hoisted(); updates expectLocation to handle multiple location test-ids via getAllByTestId. Both fixes are correct.
packages/webview-app/tests/screens/recovery/recoverySupportScreens.test.tsx Adds missing euclid mock exports (borderRadius.mdd, RecoveryPhrase); removes intermediate /recovery/success navigation assertions that didn't match actual implementation.

Sequence Diagram

sequenceDiagram
    participant Host as Native Host
    participant BridgeProvider
    participant ErrorBoundary as ErrorBoundary (wrapper)
    participant EBInner as ErrorBoundaryInner (class)
    participant App

    Host->>BridgeProvider: mount WebView
    BridgeProvider->>ErrorBoundary: render children
    ErrorBoundary->>EBInner: render with onDismiss callback
    EBInner->>App: render children

    alt No error
        App-->>Host: normal UI
    else Render error thrown
        App--xEBInner: throws Error
        EBInner->>EBInner: getDerivedStateFromError → hasError=true
        EBInner-->>Host: fallback UI (Something went wrong)
        alt User clicks Try again
            EBInner->>EBInner: setState hasError=false
            EBInner->>App: re-render children
        else User clicks Close
            EBInner->>ErrorBoundary: onDismiss()
            ErrorBoundary->>Host: lifecycle.dismiss({ reason: 'user_cancel' })
        end
    end
Loading

Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin/dev..." | Re-trigger Greptile

Comment on lines +80 to +159
const styles = {
container: {
display: 'flex',
flexDirection: 'column' as const,
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
width: '100%',
padding: 24,
backgroundColor: '#fff',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
},
content: {
display: 'flex',
flexDirection: 'column' as const,
alignItems: 'center',
maxWidth: 320,
textAlign: 'center' as const,
},
icon: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: '#FEE2E2',
color: '#DC2626',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 24,
fontWeight: 700,
marginBottom: 16,
},
title: {
fontSize: 20,
fontWeight: 600,
color: '#111',
margin: '0 0 8px',
},
message: {
fontSize: 14,
color: '#666',
lineHeight: 1.5,
margin: '0 0 24px',
},
errorDetail: {
fontSize: 11,
color: '#999',
backgroundColor: '#f5f5f5',
borderRadius: 8,
padding: 12,
width: '100%',
overflow: 'auto' as const,
maxHeight: 80,
marginBottom: 24,
textAlign: 'left' as const,
},
primaryButton: {
width: '100%',
padding: '14px 24px',
fontSize: 16,
fontWeight: 600,
color: '#fff',
backgroundColor: '#111',
border: 'none',
borderRadius: 12,
cursor: 'pointer',
marginBottom: 12,
},
secondaryButton: {
width: '100%',
padding: '14px 24px',
fontSize: 16,
fontWeight: 600,
color: '#666',
backgroundColor: 'transparent',
border: '1px solid #ddd',
borderRadius: 12,
cursor: 'pointer',
},
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Raw hex values instead of design tokens

The styles object uses raw hex literals throughout (#fff, #FEE2E2, #DC2626, #111, #666, #f5f5f5, #999, #ddd) instead of tokens from @selfxyz/euclid. Per the project rules, shared color/font/spacing tokens should be used instead of raw hex values in UI code. For example, colors.red600, colors.black, colors.grey600, etc. should replace the raw values where equivalents exist in the euclid token set.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

TunnelResultScreen no longer navigates on close. It sends a failure VerificationResult via lifecycle.setResult and then dismisses. The four close-action tests that previously asserted on post-close navigation now assert on the lifecycle.setResult payload, analytics trackEvent for tunnel_result_cancelled with the correct source, and lifecycle.dismiss being called.

Update the receipt-from-success-context test to assert close-only controls, matching TunnelProofReceiptScreen which does not currently pass an onConfirm prop. Pairs with the existing tests that hide the confirm button when backState is missing or indicates failure.
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