Skip to content

Commit e1ea995

Browse files
committed
fix(test): tightens NotificationDrawer assertions and adds coverage
1 parent 5fdc7a0 commit e1ea995

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

src/app/index.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@
9292
animation: toast-slide-out 0.3s ease-in forwards;
9393
}
9494

95-
/* Kobalte Dialog drawer animations */
95+
/* Kobalte Dialog drawer animations — uses data-attribute selectors instead of @utility
96+
because Kobalte sets [data-expanded]/[data-closed] based on open/closed state */
9697
.drawer-content[data-closed] {
9798
animation: drawer-slide-out 0.3s ease-in forwards;
9899
}

tests/components/shared/NotificationDrawer.test.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ describe("NotificationDrawer", () => {
5353
vi.advanceTimersByTime(0);
5454
expect(screen.getByText(/Something failed/)).toBeDefined();
5555
expect(screen.getByText(/api/)).toBeDefined();
56+
expect(screen.queryByText("(will retry)")).toBeNull();
5657
});
5758

5859
it("shows newest notification first in the list", () => {
@@ -108,20 +109,23 @@ describe("NotificationDrawer", () => {
108109
});
109110

110111
it("calls onClose when overlay backdrop is clicked", () => {
112+
// Kobalte dismisses via document-level capture-phase pointerdown (createInteractOutside),
113+
// not an overlay click handler. data-testid targets the overlay because Dialog.Overlay
114+
// has no ARIA role to query by.
111115
const onClose = vi.fn();
112116
render(() => <NotificationDrawer open={true} onClose={onClose} />);
113117
vi.advanceTimersByTime(0);
114118
const overlay = screen.getByTestId("notification-overlay");
115119
fireEvent.pointerDown(overlay);
116-
expect(onClose).toHaveBeenCalled();
120+
expect(onClose).toHaveBeenCalledTimes(1);
117121
});
118122

119123
it("calls onClose when X button is clicked", () => {
120124
const onClose = vi.fn();
121125
render(() => <NotificationDrawer open={true} onClose={onClose} />);
122126
vi.advanceTimersByTime(0);
123127
fireEvent.click(screen.getByLabelText("Close notifications"));
124-
expect(onClose).toHaveBeenCalled();
128+
expect(onClose).toHaveBeenCalledTimes(1);
125129
});
126130

127131
it("shows empty state text when no notifications", () => {
@@ -159,6 +163,35 @@ describe("NotificationDrawer", () => {
159163
render(() => <NotificationDrawer open={true} onClose={onClose} />);
160164
vi.advanceTimersByTime(0);
161165
fireEvent.keyDown(document, { key: "Escape" });
162-
expect(onClose).toHaveBeenCalled();
166+
expect(onClose).toHaveBeenCalledTimes(1);
167+
});
168+
169+
it("shows retryable indicator when notification is retryable", () => {
170+
pushNotification("api", "Transient failure", "error", true);
171+
renderDrawer(true);
172+
vi.advanceTimersByTime(0);
173+
expect(screen.queryByText("(will retry)")).not.toBeNull();
174+
});
175+
176+
it("marks dialog as closed when open transitions from true to false", () => {
177+
// solid-presence waits for animationend to unmount; happy-dom has no CSS engine
178+
// so the element stays in DOM with data-closed instead of being removed
179+
const { setIsOpen } = renderDrawer(true);
180+
vi.advanceTimersByTime(0);
181+
const dialog = screen.getByRole("dialog");
182+
expect(dialog.hasAttribute("data-closed")).toBe(false);
183+
setIsOpen(false);
184+
vi.advanceTimersByTime(0);
185+
expect(dialog.hasAttribute("data-closed")).toBe(true);
186+
});
187+
188+
it("has accessible description for screen readers", () => {
189+
renderDrawer(true);
190+
vi.advanceTimersByTime(0);
191+
const dialog = screen.getByRole("dialog");
192+
const descId = dialog.getAttribute("aria-describedby");
193+
expect(descId).toBeTruthy();
194+
const descEl = document.getElementById(descId!);
195+
expect(descEl?.textContent).toContain("Recent system notifications and errors");
163196
});
164197
});

0 commit comments

Comments
 (0)