|
|
|
@@ -1,11 +1,11 @@
|
|
|
|
|
import React from "react";
|
|
|
|
|
import "@testing-library/jest-dom";
|
|
|
|
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
|
|
|
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
|
|
|
|
|
|
|
|
import { api } from "./api";
|
|
|
|
|
import { setAuthUserKey } from "./auth";
|
|
|
|
|
import { clearAuthClientState, setAuthUserKey } from "./auth";
|
|
|
|
|
import SavedViewsMenu from "./components/SavedViewsMenu";
|
|
|
|
|
import { useCompanies } from "./hooks/useCompanies";
|
|
|
|
|
import { invalidateCompaniesCache, useCompanies } from "./hooks/useCompanies";
|
|
|
|
|
import { I18nProvider } from "./i18n/I18nProvider";
|
|
|
|
|
|
|
|
|
|
const mockedApi = api as jest.Mocked<typeof api>;
|
|
|
|
@@ -15,10 +15,20 @@ function CompaniesProbe() {
|
|
|
|
|
return <div>{companies.map((company) => company.name).join(", ") || "No companies"}</div>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function holdCompanyRead() {
|
|
|
|
|
let finish!: (value: { data: { id: number; name: string }[] }) => void;
|
|
|
|
|
const response = new Promise<{ data: { id: number; name: string }[] }>(resolve => {
|
|
|
|
|
finish = resolve;
|
|
|
|
|
});
|
|
|
|
|
mockedApi.get.mockReturnValueOnce(response as ReturnType<typeof api.get>);
|
|
|
|
|
return (name: string) => finish({ data: [{ id: 1, name }] });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe("account-scoped browser state", () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
window.localStorage.clear();
|
|
|
|
|
jest.clearAllMocks();
|
|
|
|
|
invalidateCompaniesCache();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("keeps saved views private to the account that created them", async () => {
|
|
|
|
@@ -54,9 +64,63 @@ describe("account-scoped browser state", () => {
|
|
|
|
|
expect(await screen.findByText("User A Company")).toBeInTheDocument();
|
|
|
|
|
|
|
|
|
|
mockedApi.get.mockResolvedValueOnce({ data: [{ id: 2, name: "User B Company" }] } as any);
|
|
|
|
|
setAuthUserKey("user-b", false);
|
|
|
|
|
act(() => setAuthUserKey("user-b", false));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(screen.getByText("User B Company")).toBeInTheDocument());
|
|
|
|
|
expect(screen.queryByText("User A Company")).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not let a pre-edit company response restore stale cache", async () => {
|
|
|
|
|
setAuthUserKey("user-a", false);
|
|
|
|
|
const finishOldRead = holdCompanyRead();
|
|
|
|
|
const oldConsumer = render(<CompaniesProbe />);
|
|
|
|
|
await waitFor(() => expect(mockedApi.get).toHaveBeenCalledTimes(1));
|
|
|
|
|
|
|
|
|
|
invalidateCompaniesCache();
|
|
|
|
|
const finishFreshRead = holdCompanyRead();
|
|
|
|
|
const freshConsumer = render(<CompaniesProbe />);
|
|
|
|
|
expect(mockedApi.get).toHaveBeenCalledTimes(2);
|
|
|
|
|
await act(async () => finishFreshRead("Current company"));
|
|
|
|
|
expect(freshConsumer.container).toHaveTextContent("Current company");
|
|
|
|
|
|
|
|
|
|
await act(async () => finishOldRead("Before the edit"));
|
|
|
|
|
oldConsumer.unmount();
|
|
|
|
|
freshConsumer.unmount();
|
|
|
|
|
const later = render(<CompaniesProbe />);
|
|
|
|
|
expect(later.container).toHaveTextContent("Current company");
|
|
|
|
|
expect(mockedApi.get).toHaveBeenCalledTimes(2);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("shares one pending company read between callers without treating initial empty state as cached", async () => {
|
|
|
|
|
setAuthUserKey("user-a", false);
|
|
|
|
|
const finishRead = holdCompanyRead();
|
|
|
|
|
const first = render(<CompaniesProbe />);
|
|
|
|
|
const second = render(<CompaniesProbe />);
|
|
|
|
|
expect(mockedApi.get).toHaveBeenCalledTimes(1);
|
|
|
|
|
|
|
|
|
|
await act(async () => finishRead("Shared company"));
|
|
|
|
|
expect(first.container).toHaveTextContent("Shared company");
|
|
|
|
|
expect(second.container).toHaveTextContent("Shared company");
|
|
|
|
|
expect(mockedApi.get).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not restore a pending company response from before logout after signing back in", async () => {
|
|
|
|
|
setAuthUserKey("user-a", false);
|
|
|
|
|
const finishOldRead = holdCompanyRead();
|
|
|
|
|
const oldConsumer = render(<CompaniesProbe />);
|
|
|
|
|
await waitFor(() => expect(mockedApi.get).toHaveBeenCalledTimes(1));
|
|
|
|
|
|
|
|
|
|
mockedApi.get.mockResolvedValueOnce({ data: [] } as Awaited<ReturnType<typeof api.get>>);
|
|
|
|
|
act(() => clearAuthClientState(false));
|
|
|
|
|
await waitFor(() => expect(mockedApi.get).toHaveBeenCalledTimes(2));
|
|
|
|
|
mockedApi.get.mockResolvedValueOnce({ data: [{ id: 1, name: "New session company" }] } as Awaited<ReturnType<typeof api.get>>);
|
|
|
|
|
act(() => setAuthUserKey("user-a", false));
|
|
|
|
|
expect(await screen.findByText("New session company")).toBeInTheDocument();
|
|
|
|
|
|
|
|
|
|
await act(async () => finishOldRead("Previous session company"));
|
|
|
|
|
oldConsumer.unmount();
|
|
|
|
|
const later = render(<CompaniesProbe />);
|
|
|
|
|
expect(later.container).toHaveTextContent("New session company");
|
|
|
|
|
expect(mockedApi.get).toHaveBeenCalledTimes(3);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|