diff --git a/src/components/__tests__/ToastProvider.test.tsx b/src/components/__tests__/ToastProvider.test.tsx
new file mode 100644
index 0000000..a684bef
--- /dev/null
+++ b/src/components/__tests__/ToastProvider.test.tsx
@@ -0,0 +1,116 @@
+import { act, fireEvent, render, screen } from "@testing-library/react";
+import { ToastProvider, useToast } from "../ToastProvider";
+
+function ToastHarness() {
+ const { push } = useToast();
+
+ return (
+
+
+
+
+
+ );
+}
+
+function MissingProviderHarness() {
+ useToast();
+ return null;
+}
+
+describe("ToastProvider", () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ act(() => {
+ jest.runOnlyPendingTimers();
+ });
+ jest.useRealTimers();
+ });
+
+ it("renders info toasts as polite status messages", () => {
+ render(
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: "Show info" }));
+
+ expect(screen.getByRole("status")).toHaveTextContent("Saved route");
+ expect(screen.getByText("Saved route").parentElement).toHaveAttribute(
+ "aria-live",
+ "polite",
+ );
+ });
+
+ it("renders error toasts with an alert role", () => {
+ render(
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: "Show error" }));
+
+ expect(screen.getByRole("alert")).toHaveTextContent("Route failed");
+ });
+
+ it("auto-dismisses toasts after four seconds", () => {
+ render(
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: "Show info" }));
+ expect(screen.getByText("Saved route")).toBeInTheDocument();
+
+ act(() => {
+ jest.advanceTimersByTime(4000);
+ });
+
+ expect(screen.queryByText("Saved route")).not.toBeInTheDocument();
+ });
+
+ it("keeps multiple queued toasts visible until their timers expire", () => {
+ render(
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: "Show two" }));
+
+ expect(screen.getByText("First route")).toBeInTheDocument();
+ expect(screen.getByText("Second route")).toBeInTheDocument();
+
+ act(() => {
+ jest.advanceTimersByTime(3999);
+ });
+
+ expect(screen.getByText("First route")).toBeInTheDocument();
+ expect(screen.getByText("Second route")).toBeInTheDocument();
+ });
+
+ it("throws a clear error when useToast is rendered outside the provider", () => {
+ const consoleError = jest.spyOn(console, "error").mockImplementation();
+
+ expect(() => render()).toThrow(
+ "useToast must be used inside ",
+ );
+
+ consoleError.mockRestore();
+ });
+});