Skip to content

TST-49: Frontend view and component coverage gaps#817

Merged
Chris0Jeky merged 11 commits intomainfrom
test/frontend-view-component-coverage
Apr 12, 2026
Merged

TST-49: Frontend view and component coverage gaps#817
Chris0Jeky merged 11 commits intomainfrom
test/frontend-view-component-coverage

Conversation

@Chris0Jeky
Copy link
Copy Markdown
Owner

Summary

  • Adds tests for 7 previously untested UI primitives: TdBadge, TdEmptyState, TdInlineAlert, TdSkeleton, TdSpinner, TdTag, TdToast
  • Adds tests for 6 previously untested shell/common components: ShellSidebar, ShellTopbar, ShellKeyboardHelp, KeyboardShortcutsHelp, ToastContainer, WorkspaceHelpCallout
  • Adds tests for 2 previously untested activity components: ActivitySelector, ActivityResults
  • Adds tests for the SwUpdatePrompt component (service worker update prompt)
  • Total: 16 new test files covering all previously untested Vue components
  • All 2061 tests pass, typecheck clean

Closes #716

Test plan

  • All new view/component tests pass with vitest (2061/2061)
  • npm run typecheck passes
  • No existing tests broken

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Self-review findings

What was covered

All 16 previously untested Vue components now have test files:

UI primitives (7): TdBadge, TdEmptyState, TdInlineAlert, TdSkeleton, TdSpinner, TdTag, TdToast
Shell components (4): ShellSidebar, ShellTopbar, ShellKeyboardHelp, SwUpdatePrompt
Common components (2): ToastContainer, KeyboardShortcutsHelp
Workspace components (1): WorkspaceHelpCallout
Activity components (2): ActivitySelector, ActivityResults

Quality checks passed

  • All tests use mount() (full mount), not shallowMount
  • Tests verify visible text content, not just element existence
  • Interaction tests present: click handlers, select changes, toggle state, emitted events
  • ARIA attributes verified for accessibility-sensitive components
  • Props, slots, conditional rendering, and edge cases (empty states, disabled states) all tested
  • Mock stores are reactive and minimal -- they don't bypass component logic

What tests did NOT cover (by design)

  • SwUpdatePrompt: Can't test actual service worker registration since registerSW is a virtual module; the mock captures the onNeedRefresh callback and verifies the component's UI response
  • ActivityResults: Virtual list is mocked because @tanstack/vue-virtual requires a real scrollable DOM; the mock renders all items directly, which tests the template logic without the scroll optimization layer
  • Teleported components (KeyboardShortcutsHelp, ShellKeyboardHelp): Tests query document.body directly since Vue's Teleport renders outside the wrapper root

Verification

  • 2063 tests passing (was 1917 before this PR)
  • npm run typecheck clean
  • No existing tests broken

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR closes frontend component coverage gaps by adding new Vitest/@vue/test-utils specs for previously untested UI primitives and shell/common/activity components, aligning with the test-coverage goals in #716.

Changes:

  • Adds 16 new unit test files covering UI primitives, shell/common components, activity components, and the PWA service-worker update prompt.
  • Introduces focused mocks for stores/composables (e.g., toast store, workspace/session stores, virtual list composable) to validate component behavior deterministically.
  • Verifies key accessibility attributes/roles across multiple components (e.g., role, aria-live, dialog semantics).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/taskdeck-web/src/tests/components/WorkspaceHelpCallout.spec.ts Adds coverage for help callout visible/dismissed states, slots, and actions.
frontend/taskdeck-web/src/tests/components/ui/TdToast.spec.ts Tests toast variants, dismiss behavior, and a11y attributes.
frontend/taskdeck-web/src/tests/components/ui/TdTag.spec.ts Tests tag slot rendering, removable behavior, and custom color styling.
frontend/taskdeck-web/src/tests/components/ui/TdSpinner.spec.ts Tests spinner label/size defaults, SVG structure, and role="status".
frontend/taskdeck-web/src/tests/components/ui/TdSkeleton.spec.ts Tests skeleton sizing defaults, rounded/circle behavior, and aria-hidden.
frontend/taskdeck-web/src/tests/components/ui/TdInlineAlert.spec.ts Tests alert variants, dismissibility, emitted events, and a11y role.
frontend/taskdeck-web/src/tests/components/ui/TdEmptyState.spec.ts Tests title/description rendering and icon/action slots.
frontend/taskdeck-web/src/tests/components/ui/TdBadge.spec.ts Tests badge variants/sizes and slot rendering.
frontend/taskdeck-web/src/tests/components/ToastContainer.spec.ts Tests toast store integration, roles for error toasts, and close behavior.
frontend/taskdeck-web/src/tests/components/SwUpdatePrompt.spec.ts Tests SW update prompt lifecycle, update/dismiss actions, and a11y attributes.
frontend/taskdeck-web/src/tests/components/ShellTopbar.spec.ts Tests workspace mode selector, mode descriptions, and emitted palette event.
frontend/taskdeck-web/src/tests/components/ShellSidebar.spec.ts Tests nav rendering across modes, feature-flag behavior, badges, and emitted events.
frontend/taskdeck-web/src/tests/components/ShellKeyboardHelp.spec.ts Tests teleported keyboard help dialog rendering and close behavior.
frontend/taskdeck-web/src/tests/components/KeyboardShortcutsHelp.spec.ts Tests teleported shortcuts dialog content and close actions.
frontend/taskdeck-web/src/tests/components/ActivitySelector.spec.ts Tests selector controls, emitted updates, fetch/copy actions, and conditional UI.
frontend/taskdeck-web/src/tests/components/ActivityResults.spec.ts Tests loading/empty/timeline states with mocked virtual list and formatting helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +110 to +122
it('uses custom dismiss and replay labels', () => {
visibleRef.value = true
const wrapper = mount(WorkspaceHelpCallout, {
props: {
topic: 'board',
title: 'Board',
description: 'Desc',
dismissLabel: 'Got it',
},
})
const dismissBtn = wrapper.findAll('button').find((b) => b.text().includes('Got it'))
expect(dismissBtn).toBeDefined()
})
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

Test case name says it "uses custom dismiss and replay labels", but the test only sets/asserts dismissLabel (no replayLabel is provided or verified). Either rename the test to match what it covers, or also pass replayLabel and assert it appears in the dismissed state to keep intent aligned with coverage.

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +7
vi.mock('../../composables/useEscapeStack', () => ({
registerEscapeHandler: vi.fn(() => vi.fn()),
}))
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

This spec mocks ../../composables/useEscapeStack, but ShellKeyboardHelp.vue does not import or use that composable. The unused mock adds noise and can be removed to keep the test focused on the component’s actual dependencies.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive suite of unit tests for various frontend components using Vitest and Vue Test Utils. The tests cover activity-related components, shell layout elements like the sidebar and topbar, UI primitives, and utility components. Feedback focuses on improving test safety in ActivityResults by replacing non-null assertions with explicit existence checks and reducing boilerplate in the ShellSidebar tests by implementing a mounting helper function.

Comment on lines +81 to +82
const reviewBtn = wrapper.findAll('button').find((b) => b.text() === 'Open Review')
await reviewBtn!.trigger('click')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using a non-null assertion (!) on the result of find can lead to cryptic errors if the button is not found. It's safer to explicitly assert that the button exists before triggering an event.

Suggested change
const reviewBtn = wrapper.findAll('button').find((b) => b.text() === 'Open Review')
await reviewBtn!.trigger('click')
const reviewBtn = wrapper.findAll('button').find((b) => b.text() === 'Open Review')
expect(reviewBtn).toBeDefined()
await reviewBtn!.trigger('click')

Comment on lines +46 to +50
it('renders the Taskdeck brand title', () => {
const wrapper = mount(ShellSidebar, {
props: { isAuthenticated: true },
global: { stubs: { 'router-link': { template: '<a><slot /></a>', props: ['to'] } } },
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The router-link stub and mounting configuration are repeated across many tests in this file. Creating a helper function for mounting the component would significantly reduce boilerplate and improve maintainability.

Suggested change
it('renders the Taskdeck brand title', () => {
const wrapper = mount(ShellSidebar, {
props: { isAuthenticated: true },
global: { stubs: { 'router-link': { template: '<a><slot /></a>', props: ['to'] } } },
})
const mountSidebar = (props = { isAuthenticated: true }) => mount(ShellSidebar, {
props,
global: { stubs: { 'router-link': { template: '<a><slot /></a>', props: ['to'] } } },
})
it('renders the Taskdeck brand title', () => {
const wrapper = mountSidebar()

Copy link
Copy Markdown
Owner Author

@Chris0Jeky Chris0Jeky left a comment

Choose a reason for hiding this comment

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

Review: Quality improvements for new test files

Good coverage addition overall -- 16 test files with meaningful assertions across all component categories. The tests go well beyond "renders without crashing" and check interactions, slots, props, emissions, and accessibility attributes. Here are the issues I'm fixing:

Bot-flagged issues (all confirmed)

  1. WorkspaceHelpCallout.spec.ts line 110: Test named "uses custom dismiss and replay labels" only tests dismissLabel -- never passes or asserts replayLabel. Will add replayLabel coverage in the dismissed state.

  2. ShellKeyboardHelp.spec.ts line 5-7: Mocks useEscapeStack but the component (ShellKeyboardHelp.vue) does not import or use it. Removing the unused mock.

  3. ActivityResults.spec.ts line 82: Non-null assertion (!) on .find() result can produce cryptic runtime errors when the element is not found. Fixing across all new files.

Additional findings from adversarial review

  1. Non-null assertions without guard across multiple files: The reviewBtn!.trigger('click') pattern appears in ActivityResults (lines 82, 86), ActivitySelector (115, 124, 148), WorkspaceHelpCallout (60, 88), and KeyboardShortcutsHelp (97). In each case the preceding expect(...).toBeDefined() is not sufficient because TypeScript still sees | undefined. Will replace with explicit expect(btn).toBeTruthy() followed by a guarded call, or use if (!btn) throw ... pattern.

  2. ShellSidebar.spec.ts line 216: inboxItem?.classes() uses optional chaining, but if the element is missing, expect(undefined).toContain(...) gives an unclear failure message. Will add existence assertion.

  3. ShellSidebar.spec.ts: Repeated router-link stub definition in every test (12 times). Extracting to a shared mountSidebar() helper to reduce boilerplate and make tests easier to maintain.

All fixes incoming as focused commits.

…ve non-null assertions

Split "uses custom dismiss and replay labels" into two focused tests that
each cover one prop. Add missing replayLabel assertion in dismissed state.
Replace non-null assertions with toBeTruthy() + optional chaining.
ShellKeyboardHelp.vue does not import useEscapeStack, so the mock
served no purpose and was misleading about the component's dependencies.
…s test

Use toBeTruthy() to assert element existence before interacting, then
optional chaining instead of ! to avoid cryptic runtime errors if the
element is unexpectedly missing.
…or test

Add explicit existence checks before interacting with dynamically found
buttons. Use optional chaining instead of non-null assertions.
…sHelp test

Use toBeTruthy() + optional chaining for the Got it button interaction
to produce a clear failure message if the element is missing.
Replace 14 repeated inline mount+stubs configurations with a shared
mountSidebar() helper. Add explicit existence assertion for the active
route test to prevent unclear failures from optional chaining.
@Chris0Jeky Chris0Jeky merged commit 6f47d6c into main Apr 12, 2026
24 checks passed
@github-project-automation github-project-automation bot moved this from Pending to Done in Taskdeck Execution Apr 12, 2026
@Chris0Jeky Chris0Jeky deleted the test/frontend-view-component-coverage branch April 12, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

TST-49: Frontend view and component coverage gaps — untested views and critical components

2 participants