Skip to content

fix(onboarding): hide DictationDemo when sample assets are absent - #153

Merged
debpalash merged 1 commit into
mainfrom
feat/gate-dictation-demo
May 29, 2026
Merged

fix(onboarding): hide DictationDemo when sample assets are absent#153
debpalash merged 1 commit into
mainfrom
feat/gate-dictation-demo

Conversation

@debpalash

@debpalash debpalash commented May 29, 2026

Copy link
Copy Markdown
Owner

Follow-up to #119/#133 onboarding demos.

Problem

DubbingDemo and DemoPresetGrid already hide themselves when their assets / is_demo profiles are missing. DictationDemo didn't — it always rendered its three hardcoded cards, which then fail on click without the bundled sample WAVs (/demo_audio/dictation/*.wav, rendered by scripts/build_demos.sh and absent in a plain source checkout).

Fix

Mount-time HEAD probe of the first sample; if it's not present, the component returns null (hides the whole demo), mirroring DubbingDemo's missing-manifest behavior. With assets present, behavior is unchanged.

Test

DictationDemo.test.jsx: HEAD 404 → demo renders nothing (existing 3 tests still pass).

No default-behavior change (when assets ship, the demo shows as before), no version bump.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • The dictation demo now automatically hides itself when required audio demonstration assets are unavailable, preventing users from encountering incomplete demo experiences or attempting to use non-functional demo features.
  • Tests

    • Added test coverage to validate that the demo properly hides itself when the necessary audio demonstration assets cannot be accessed or located on startup.

Review Change Stack

… follow-up)

DubbingDemo and DemoPresetGrid already degrade gracefully (hide) when their
assets / is_demo profiles are missing, but DictationDemo always rendered its
three hardcoded cards — which fail on click without the bundled sample WAVs
(rendered by scripts/build_demos.sh; absent in a plain source checkout).

Add a mount-time HEAD probe of the first sample; if it's not present, hide
the whole demo (mirrors DubbingDemo's missing-manifest behavior). When assets
are present, behavior is unchanged.

Test: HEAD 404 → demo renders nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

DictationDemo component now detects bundled WAV assets on mount via HEAD request, storing availability in state. When assets are missing, the component renders nothing. A test verifies this graceful degradation when asset probing returns 404.

Changes

Asset Availability Detection

Layer / File(s) Summary
Asset probe and conditional rendering
frontend/src/components/DictationDemo.jsx
Component state adds assetsAvailable flag. A mount effect performs a HEAD request to the first demo WAV URL with a cancellation token to prevent stale state updates. Render logic returns null when assets are unavailable, hiding the entire demo UI.
Asset detection test
frontend/src/test/DictationDemo.test.jsx
Test mocks global.fetch to return 404 status, renders DictationDemo, waits for the container to become empty, and asserts that the demo card content is not present.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers Problem, Fix, and Test sections with clear explanations. However, it lacks the structured template sections including Summary, Changes list, Type checkbox, Testing details, Checklist, and Screenshots as specified in the repository template. Restructure the description to follow the provided template format: add explicit Summary, Changes, Type, Testing, and Checklist sections with appropriate checkboxes and confirmations.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title clearly and concisely describes the main change: adding logic to hide DictationDemo when sample assets are absent, matching the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feat/gate-dictation-demo

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@greptile-apps

greptile-apps Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a mount-time HEAD probe to DictationDemo so it hides itself when the bundled sample WAVs are absent (e.g. a source checkout without a render step), matching the existing hide-on-missing-assets behavior of DubbingDemo and DemoPresetGrid. A new test verifies that a HEAD 404 causes the component to render nothing.

  • DictationDemo.jsx: adds assetsAvailable state, a useEffect that HEAD-checks SCRIPTS[0].wav on mount, and an early return null when the probe fails.
  • DictationDemo.test.jsx: adds a waitFor-based test for the 404 path; existing tests are unchanged but now leave the probe fetch uncontrolled since they don't mock global.fetch.

Confidence Score: 4/5

Safe to merge — the core fix is correct and the new behavior (hiding the demo when assets are absent) is well-contained with no impact on the existing happy path.

The probe logic and early return are sound. The two concerns are minor: the component renders the full card UI for the duration of the probe, so a slow server can produce a brief visible flash before the demo hides itself; and the existing synchronous tests don't mock global.fetch, leaving a dangling async state update that may surface as act() warnings without causing actual test failures.

Both changed files are straightforward; DictationDemo.test.jsx is the one worth a second look to ensure the uncontrolled fetch in the older tests doesn't produce noise in CI.

Important Files Changed

Filename Overview
frontend/src/components/DictationDemo.jsx Adds a mount-time HEAD probe of the first WAV asset; hides component when assets are absent. The null state (probing in-flight) renders the full demo UI rather than hiding it, which can cause a brief visible flash before the component disappears.
frontend/src/test/DictationDemo.test.jsx New 404-probe test is correct and uses waitFor properly. Existing synchronous tests now leave the HEAD fetch uncontrolled, relying on assertions completing before the async resolution — technically passing but fragile against act() warnings.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant DictationDemo
    participant Server

    Browser->>DictationDemo: "mount (assetsAvailable = null)"
    Note over DictationDemo: renders full UI while probing
    DictationDemo->>Server: HEAD /demo_audio/dictation/en_conversational.wav
    alt assets present (200 OK)
        Server-->>DictationDemo: 200 OK
        DictationDemo->>DictationDemo: setAssetsAvailable(true)
        Note over DictationDemo: renders demo cards normally
    else assets absent (404 / network error)
        Server-->>DictationDemo: 404 / error
        DictationDemo->>DictationDemo: setAssetsAvailable(false)
        DictationDemo->>Browser: return null (component hidden)
    end
Loading

Comments Outside Diff (1)

  1. frontend/src/test/DictationDemo.test.jsx, line 24-35 (link)

    P2 Existing tests leave an uncontrolled async fetch in-flight

    'renders the three bundled scripts' and 'shows the "no hotkey" warning...' do not mock global.fetch. The new mount-time HEAD probe fires on every render, so these tests now issue a real (or uncaught) network request. Because the assertions are synchronous they pass before the Promise chain resolves, but the subsequent setAssetsAvailable(false) call races against @testing-library/react's cleanup and can produce act() warnings. Adding a { ok: true } fetch mock to beforeEach (analogous to what the probe test does explicitly) would make the probe deterministic for all tests.

    Fix in Claude Code

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(onboarding): hide DictationDemo when..." | Re-trigger Greptile

Comment on lines +68 to +79
const [assetsAvailable, setAssetsAvailable] = useState(null);
const audioRef = useRef(null);

// Probe whether the bundled dictation samples actually exist; hide the whole
// demo if not, mirroring DubbingDemo's missing-manifest behavior.
useEffect(() => {
let cancelled = false;
fetch(`${API}${SCRIPTS[0].wav}`, { method: 'HEAD' })
.then((r) => { if (!cancelled) setAssetsAvailable(r.ok); })
.catch(() => { if (!cancelled) setAssetsAvailable(false); });
return () => { cancelled = true; };
}, []);

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 Flash of content during asset probe

While assetsAvailable is null (probe in-flight), the full demo — all three script cards, audio element, and hotkey effects — renders and is visible to the user. If the HEAD probe resolves to false, a brief flash of the unusable cards plays before the component hides itself. On a slow or lagging local server, the flash can be noticeable. Since the intent is to avoid ever showing the broken cards, returning null (or a lightweight placeholder) while the probe is pending would close the window entirely.

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!

Fix in Claude Code

@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.

🧹 Nitpick comments (1)
frontend/src/test/DictationDemo.test.jsx (1)

16-35: ⚡ Quick win

Stub fetch by default so the mount probe doesn't issue a real network call.

With the new mount probe, every render of DictationDemo now fires fetch(HEAD). The first two tests (Lines 24-29 and 31-35) don't set global.fetch, so the probe hits the real configured API base. The synchronous assertions still pass, but this introduces a real outbound request (rejecting in CI) and a possible unhandled-rejection/act warning — making these tests less deterministic. Add a default mock in beforeEach.

♻️ Default the probe to a benign mock
   beforeEach(() => {
     originalFetch = global.fetch;
+    // Default: asset probe resolves "present" so renders are deterministic
+    // and no real network call leaks out. Tests override as needed.
+    global.fetch = vi.fn(() => Promise.resolve({ ok: true, status: 200 }));
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/test/DictationDemo.test.jsx` around lines 16 - 35, Tests render
of DictationDemo triggers the new mount probe which calls fetch(HEAD), causing
real network requests; stub global.fetch in the existing beforeEach to a benign
mock that returns a resolved Promise/Response (e.g., a minimal OK-like response)
so the probe never issues a real outbound call, and keep the existing afterEach
that restores originalFetch; implement the mock in the beforeEach near the
existing originalFetch assignment and ensure it handles the HEAD probe used by
the mount probe so the synchronous assertions in the tests for DictationDemo
succeed deterministically.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@frontend/src/test/DictationDemo.test.jsx`:
- Around line 16-35: Tests render of DictationDemo triggers the new mount probe
which calls fetch(HEAD), causing real network requests; stub global.fetch in the
existing beforeEach to a benign mock that returns a resolved Promise/Response
(e.g., a minimal OK-like response) so the probe never issues a real outbound
call, and keep the existing afterEach that restores originalFetch; implement the
mock in the beforeEach near the existing originalFetch assignment and ensure it
handles the HEAD probe used by the mount probe so the synchronous assertions in
the tests for DictationDemo succeed deterministically.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2811dc7e-fbbf-479a-b8c6-9efa8c8ab4c3

📥 Commits

Reviewing files that changed from the base of the PR and between 79473c4 and bf608f8.

📒 Files selected for processing (2)
  • frontend/src/components/DictationDemo.jsx
  • frontend/src/test/DictationDemo.test.jsx

@debpalash
debpalash merged commit 034d1c4 into main May 29, 2026
15 checks passed
@debpalash
debpalash deleted the feat/gate-dictation-demo branch May 29, 2026 17:44
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