Add specs for modal, image, and data-table sort components - #656
Add specs for modal, image, and data-table sort components#656fpigeonjr wants to merge 2 commits into
Conversation
- Un-skip modal.spec.ts (pre-existing tests already pass under Vitest) and add specs for reopen guard, closeModal(false), Escape-key close/no-close branches, modalElIds construction, selectedType class, and the Tab focus-trap keydown listener. - Add specs for image.component.ts covering ngOnInit's file-picker, save/cancel, and drag-and-drop wiring. - Add sort.directive.spec.ts covering register/deregister, duplicate and missing id errors, and sort direction cycling with disableClear overrides. Coverage: modal.component.ts 37.14% -> 99.04%, image.component.ts 45.61% -> 92.98%, sort.directive.ts 28.12% -> 100% (line coverage). Closes #632
There was a problem hiding this comment.
Pull request overview
This PR increases unit-test coverage in the src/ui-kit/components/ area by adding/expanding Vitest specs for the modal and image components and adding a new spec suite for the data-table sort directive, aligning with the coverage epic goals.
Changes:
- Unskips and significantly expands
SamModalComponentspecs (open/close behavior, escape handling, id/type branches, focus trapping). - Adds DOM-event-driven specs for
SamImageComponent(file input change/save/cancel, drag-and-drop behaviors). - Introduces a new spec file for
SamSortDirectivecovering registration, sorting cycles, and disableClear behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/ui-kit/components/modal/modal.spec.ts |
Unskips and expands modal specs; adds behavioral coverage for open/close, escape handling, and focus-trap logic. |
src/ui-kit/components/image/image.spec.ts |
Adds event-driven specs covering file selection, save/cancel flows, and drag/drop handling. |
src/ui-kit/components/data-table/sort.directive.spec.ts |
Adds a new unit test suite validating sort directive registration and direction-cycling behavior. |
Suppressed comments (6)
src/ui-kit/components/modal/modal.spec.ts:180
- This
KeyboardEventinitialization useskeyCodein the init dict, which is not reliably honored; the listener checksev.keyCode === 9. DefinekeyCode/shiftKeyexplicitly so the focus-wrap assertion is meaningful.
firstFocusEl.dispatchEvent(
new KeyboardEvent("keydown", { keyCode: 9, shiftKey: true })
);
src/ui-kit/components/modal/modal.spec.ts:186
- Same issue here:
keyCodepassed to theKeyboardEventconstructor is not reliably reflected on the event object, so theev.keyCode === 9branch may never run. DefinekeyCode/shiftKeyexplicitly.
lastFocusEl.dispatchEvent(
new KeyboardEvent("keydown", { keyCode: 9, shiftKey: false })
);
src/ui-kit/components/modal/modal.spec.ts:226
- The "single focusable element" assertion depends on
ev.keyCode === 9, butnew KeyboardEvent(..., { keyCode: 9 })often yieldskeyCode === 0. Use anEventwith a definedkeyCodesopreventDefault()is actually exercised.
const event = new KeyboardEvent("keydown", {
keyCode: 9,
shiftKey: false,
});
const preventDefaultSpy = vi.spyOn(event, "preventDefault");
modalContentEl.dispatchEvent(event);
src/ui-kit/components/image/image.spec.ts:110
- This spec relies on
setTimeout(10)to wait forFileReader/RxJS processing, which is nondeterministic. MockreadAsDataURLto fireonloadimmediately so the drop test doesn’t depend on timing.
containerEl.dispatchEvent(dropEvent);
await new Promise((resolve) => setTimeout(resolve, 10));
src/ui-kit/components/modal/modal.spec.ts:207
KeyboardEventinit dictkeyCodeisn’t reliably supported; this can make the Shift+Tab branch inset5082()not run under jsdom/browsers. DefinekeyCode/shiftKeyexplicitly on a dispatched event.
modalContentEl.dispatchEvent(
new KeyboardEvent("keydown", { keyCode: 9, shiftKey: true })
);
src/ui-kit/components/image/image.spec.ts:84
- This spec uses
setTimeout(10)to wait forFileReaderasync completion, which is timing-dependent. MockreadAsDataURLto triggeronloadsynchronously and remove the sleep.
fileInputEl.dispatchEvent(new Event("change"));
await new Promise((resolve) => setTimeout(resolve, 10));
expect(component.getFileName()).toBe("washington.png");
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Add an afterEach in modal.spec.ts's rendered-tests block to close the modal (or ngOnDestroy) and remove the modal-open body class, preventing leaked global state between tests. - Replace image.spec.ts's setTimeout(10) waits with a FileReader.prototype.readAsDataURL spy that fires onload synchronously, so the file-upload and drag-and-drop specs are deterministic instead of timing-dependent.
Description
Raises unit-test coverage for three
src/ui-kit/components/files called out in the parent coverage epic (#576) as the largest individual gaps:modal.component.ts,image.component.ts, andsort.directive.ts(data-table).src/ui-kit/components/modal/modal.spec.ts— un-skips the pre-existingdescribe.skipwrapper (dating to a 2018 Karma-flake workaround, Skip Broken Tests #164); those 3 tests already pass unmodified under the current Vitest harness. Adds 10 new specs covering previously-uncovered public behaviour: theopenModalre-open guard,closeModal(false)(no emit), thecloseEscapeEscape-key branches (closeOnEscapetrue/false, non-Escape keys),setModalElementIds/modalElIdsconstruction, theselectedTypealert-class branch, and theset5082Tab-focus-trap keydown listeners (forward/backward wrap and the single-focusable-element case), which were entirely uncovered before this PR.src/ui-kit/components/image/image.spec.ts— adds 5 new specs drivingngOnInit's wired-up RxJS streams through simulated DOM events: file-pickerchange→ save-button click →fileChangeemit; cancel-button click clearing tmp state; drag-and-drop (onDragEnter/onDragOver/onDropEvent) setting the tmp file only while in edit mode.src/ui-kit/components/data-table/sort.directive.spec.ts(new file) —SamSortDirectivehad no dedicated spec at all (only exercised indirectly via a fully commented-outdata-table.spec.ts). Adds 12 specs against its public API:register/deregister, duplicate-id and missing-id errors,sort()activation and direction cycling (asc→desc→clear and desc→asc→clear),disableClearoverrides at the directive vs. sortable level, and thegetNextSortDirectionno-sortable guard.All new specs test through each component's/directive's public API (inputs, outputs, DOM events) — no private-method testing.
Coverage (line %, before → after, measured via
test-app/coverage/coverage-summary.json):modal.component.tsimage.component.tssort.directive.tsGlobal coverage-floor gate metrics all moved up (statements 53.56%→55.10%, branches 39.77%→41.39%, functions 50.31%→51.53%, lines 52.84%→54.43%). Per repo convention,
coverage-floor.jsonis not touched in this PR — raising the ratchet floor is a separate, deliberatenpm run coverage:bumpcommit.Motivation and Context
Closes #632
Type of Change (Select One and Apply Label)
bugfixlabelenhancementlabelbreakinglabelmaintenancelabelHow to Test
npm ci && npm ci --prefix test-appnpm --prefix test-app test— runs the full spec suite via Vitest with coverage.npm run coverage:check— verifies the coverage-floor gate still passes.npm run lint && npm run format:check— no new lint/format issues introduced.Expected result:
Test Files 107 passed (107)/Tests 697 passed (697)(up from 106/667 onmaster), coverage-floor gate passes at statements 55.10% / branches 41.39% / functions 51.53% / lines 54.43% (all above the recorded floors), andmodal.component.ts/image.component.ts/sort.directive.tsline coverage measured at 99.04% / 92.98% / 100% respectively intest-app/coverage/coverage-summary.json.Screenshots (if appropriate)
N/A — test-only changes, no UI/behavioral changes.
Checklist
gh-<number>-<slug>)format:checkpasses (npm run format:check)lintpasses (npm run lint)buildpasses (cd test-app && npm run build)cd test-app && npm test)