import { createNavigationMock, renderWithUser } from "@repo/test-utils";
import { screen, waitFor } from "@testing-library/react";
import { useEffect, useRef, useState } from "react";
import { beforeEach, describe, expect, test } from "vitest";
import {
type LocationStateDefinition,
useLocationGetKey,
useLocationGetState,
useLocationSetState,
useLocationState,
useLocationStateValue,
} from "./hooks";
import { LocationStateProvider } from "./provider";
import { LOCATION_KEY_PREFIX } from "./stores";
const mockNavigation = createNavigationMock("/");
// @ts-expect-error
globalThis.navigation = mockNavigation;
beforeEach(() => {
mockNavigation.navigate("/");
sessionStorage.clear();
});
describe(useLocationState, () => {
function LocationSyncCounter() {
const [count, setCount] = useLocationState({
name: "count",
defaultValue: 0,
storeName: "session",
});
return (
count: {count}
);
}
function LocationSyncCounterPage() {
return (
);
}
test("The `count` can be updated.", async () => {
// Arrange
const { user } = renderWithUser();
// Act
await user.click(await screen.findByRole("button", { name: "increment" }));
// Assert
expect(screen.getByRole("heading")).toHaveTextContent("count: 1");
});
test("The `count` can be updated with updater.", async () => {
// Arrange
const { user } = renderWithUser();
// Act
await user.click(
await screen.findByRole("button", { name: "increment with updater" }),
);
// Assert
expect(screen.getByRole("heading")).toHaveTextContent("count: 1");
});
test("The `count` is reset at navigation.", async () => {
// Arrange
const { user } = renderWithUser();
await user.click(await screen.findByRole("button", { name: "increment" }));
// Act
mockNavigation.navigate("/anywhere");
// Assert
await waitFor(() =>
expect(screen.getByRole("heading")).toHaveTextContent("count: 0"),
);
});
test("If there is a value in `sessionStorage`, it will be restored as the initial value.", async () => {
// Arrange
const key = mockNavigation.currentEntry?.key as string;
sessionStorage.setItem(
`${LOCATION_KEY_PREFIX}::value:${key}`,
`{"count":2}`,
);
// Act
renderWithUser();
// Assert
await waitFor(() =>
expect(screen.getByRole("heading")).toHaveTextContent("count: 2"),
);
});
});
describe(useLocationStateValue, () => {
function LocationSyncCounter() {
const counter: LocationStateDefinition = {
name: "count",
defaultValue: 0,
storeName: "session",
};
const count = useLocationStateValue(counter);
return count: {count}
;
}
function LocationSyncCounterPage() {
return (
);
}
test("If there is a value in `sessionStorage`, it will be restored as the initial value.", async () => {
// Arrange
const key = mockNavigation.currentEntry?.key as string;
sessionStorage.setItem(
`${LOCATION_KEY_PREFIX}::value:${key}`,
`{"count":2}`,
);
// Act
renderWithUser();
// Assert
await waitFor(() =>
expect(screen.getByRole("heading")).toHaveTextContent("count: 2"),
);
});
});
describe(useLocationSetState, () => {
function LocationSyncCounter() {
const counter: LocationStateDefinition = {
name: "counter",
defaultValue: 0,
storeName: "session",
};
const rendered = useRef(1);
const setCount = useLocationSetState(counter);
useEffect(() => {
rendered.current++;
}, []);
return (
rendered: {rendered.current}
);
}
function LocationSyncCounterPage() {
return (
);
}
test("`setCount` does not re-render.", async () => {
// Arrange
const { user } = renderWithUser();
// Act
await user.click(await screen.findByRole("button", { name: "increment" }));
// Assert
expect(screen.getByRole("heading")).toHaveTextContent("rendered: 1");
});
});
describe(useLocationGetState, () => {
function LocationSyncOnceCounter() {
const counter: LocationStateDefinition = {
name: "count",
defaultValue: 0,
storeName: "session",
};
const getState = useLocationGetState(counter);
const [, setState] = useState(false);
return (
<>
count: {getState()}
>
);
}
function LocationSyncCounterPage() {
return (
);
}
test("If there is a value in `sessionStorage`, it is not re-rendered.", async () => {
// Arrange
const key = mockNavigation.currentEntry?.key as string;
sessionStorage.setItem(
`${LOCATION_KEY_PREFIX}::value:${key}`,
`{"count":2}`,
);
// Act
renderWithUser();
// Assert
await waitFor(() =>
expect(screen.getByRole("heading")).toHaveTextContent("count: 0"),
);
});
test("Always get the latest values.", async () => {
// Arrange
const key = mockNavigation.currentEntry?.key as string;
sessionStorage.setItem(
`${LOCATION_KEY_PREFIX}::value:${key}`,
`{"count":2}`,
);
const { user } = renderWithUser();
// Act
await user.click(
await screen.findByRole("button", { name: "force render" }),
);
// Assert
expect(screen.getByRole("heading")).toHaveTextContent("count: 2");
});
});
describe(useLocationGetKey, () => {
function LocationSyncKeyDisplay() {
const getLocationKey = useLocationGetKey();
const [key, setKey] = useState();
return (
key: {key ?? "undefined"}
);
}
function LocationSyncKeyPage() {
return (
);
}
test("Returns a function that gets the current location key", async () => {
// Arrange
const { user } = renderWithUser();
// Act
await user.click(await screen.findByRole("button", { name: "get key" }));
// Assert
expect(screen.getByRole("heading")).not.toHaveTextContent("key: undefined");
});
test("Does not cause re-renders when location key changes", async () => {
// Arrange
const { user } = renderWithUser();
await user.click(screen.getByRole("button", { name: "get key" }));
const initialKey = screen.getByRole("heading", { level: 1 }).textContent;
// Act
mockNavigation.navigate("/new-path");
// Assert
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
initialKey!,
);
});
test("Always gets the latest key when re-render", async () => {
// Arrange
const initialKey = mockNavigation.currentEntry?.key as string;
const { user } = renderWithUser();
mockNavigation.navigate("/new-path");
// Act
await user.click(screen.getByRole("button", { name: "get key" }));
// Assert
const displayedKey = screen.getByRole("heading", { level: 1 }).textContent;
expect(displayedKey).not.toContain("undefined");
expect(displayedKey).not.toBe(`key: ${initialKey}`); // Should be different from initial key
});
});