chore: finish frontend ESLint migration #18
Enable every plugin recommended preset and replace Biome with ESLint formatting. Preserve API contracts and JSX whitespace; verify source, tests, builds and tooling.
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
import type { FC, PropsWithChildren } from "react";
|
||||
|
||||
import { QueryClientProvider, useQueryClient } from "@tanstack/react-query";
|
||||
import { act, renderHook, waitFor } from "@testing-library/react";
|
||||
import { HttpResponse, http } from "msw";
|
||||
import type { FC, PropsWithChildren } from "react";
|
||||
import { http, HttpResponse } from "msw";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { apiClient, apiQueryClient, useAuthenticatedQuery } from "@/shared/api";
|
||||
import { createTestQueryClient } from "@/test/query";
|
||||
import { resource } from "@/test/resource";
|
||||
import { server } from "@/test/server";
|
||||
|
||||
import { AuthProvider } from "./auth";
|
||||
import { useAuth } from "./auth-context";
|
||||
|
||||
@@ -14,14 +17,22 @@ const bankCardsKey = apiQueryClient.queryOptions("get", "/api/v1/bank-cards", {
|
||||
params: { query: { includeArchived: "false" } },
|
||||
}).queryKey;
|
||||
|
||||
function createAuthWrapper(queryClient: ReturnType<typeof createTestQueryClient>): FC<PropsWithChildren> {
|
||||
return props => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{props.children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
describe("AuthProvider", () => {
|
||||
it("authorizes requests after restoring the initial session", async () => {
|
||||
let authorization: string | null = null;
|
||||
let authorization: null | string = /* eslint-disable-line unicorn/no-null -- Exercise the explicit null returned by the production API/DOM contract. */ null;
|
||||
server.use(
|
||||
http.post("*/api/v1/auth/refresh", () =>
|
||||
HttpResponse.json({
|
||||
accessToken: "restored-token",
|
||||
user: { id: "user-1", email: "user@example.com" },
|
||||
user: { email: "user@example.com", id: "user-1" },
|
||||
}),
|
||||
),
|
||||
http.get("*/api/v1/brokerage-accounts", ({ request }) => {
|
||||
@@ -30,16 +41,8 @@ describe("AuthProvider", () => {
|
||||
}),
|
||||
);
|
||||
const queryClient = createTestQueryClient();
|
||||
const wrapper: FC<PropsWithChildren> = (props) => {
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
const { result, rerender } = renderHook(
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { rerender, result } = renderHook(
|
||||
() => ({ ...useAuth(), queryClient: useQueryClient() }),
|
||||
{ wrapper },
|
||||
);
|
||||
@@ -61,21 +64,13 @@ describe("AuthProvider", () => {
|
||||
const queryClient = createTestQueryClient();
|
||||
queryClient.setQueryData(["cached-query"], { value: "stale" });
|
||||
queryClient.setQueryData(bankCardsKey, [{ id: "bank-card-1" }]);
|
||||
const wrapper: FC<PropsWithChildren> = (props) => {
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.ready).toBe(true));
|
||||
|
||||
expect(result.current.user).toBeNull();
|
||||
expect(result.current.accessToken).toBeNull();
|
||||
expect(result.current.user).toBeUndefined();
|
||||
expect(result.current.accessToken).toBeUndefined();
|
||||
expect(queryClient.getQueryData(["cached-query"])).toBeUndefined();
|
||||
expect(queryClient.getQueryData(bankCardsKey)).toBeUndefined();
|
||||
});
|
||||
@@ -88,7 +83,7 @@ describe("AuthProvider", () => {
|
||||
return refreshCalls === 1
|
||||
? HttpResponse.json({
|
||||
accessToken: "restored-token",
|
||||
user: { id: "user-1", email: "user@example.com" },
|
||||
user: { email: "user@example.com", id: "user-1" },
|
||||
})
|
||||
: HttpResponse.error();
|
||||
}),
|
||||
@@ -99,15 +94,7 @@ describe("AuthProvider", () => {
|
||||
const queryClient = createTestQueryClient();
|
||||
queryClient.setQueryData(["cached-query"], { value: "stale" });
|
||||
queryClient.setQueryData(bankCardsKey, [{ id: "bank-card-1" }]);
|
||||
const wrapper: FC<PropsWithChildren> = (props) => {
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
|
||||
await waitFor(() =>
|
||||
@@ -116,8 +103,8 @@ describe("AuthProvider", () => {
|
||||
const response = await apiClient.GET("/api/v1/brokerage-accounts");
|
||||
|
||||
expect(response.response.status).toBe(401);
|
||||
await waitFor(() => expect(result.current.user).toBeNull());
|
||||
expect(result.current.accessToken).toBeNull();
|
||||
await waitFor(() => expect(result.current.user).toBeUndefined());
|
||||
expect(result.current.accessToken).toBeUndefined();
|
||||
expect(queryClient.getQueryData(["cached-query"])).toBeUndefined();
|
||||
expect(queryClient.getQueryData(bankCardsKey)).toBeUndefined();
|
||||
});
|
||||
@@ -127,7 +114,7 @@ describe("AuthProvider", () => {
|
||||
http.post("*/api/v1/auth/refresh", () =>
|
||||
HttpResponse.json({
|
||||
accessToken: "test-token",
|
||||
user: { id: "user-1", email: "user@example.com" },
|
||||
user: { email: "user@example.com", id: "user-1" },
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -147,14 +134,7 @@ describe("AuthProvider", () => {
|
||||
});
|
||||
queryClient.setQueryData(portfolioKey, { source: "current-connection" });
|
||||
queryClient.setQueryData(bankCardsKey, [{ id: "bank-card-1" }]);
|
||||
const wrapper: FC<PropsWithChildren> = (props) => {
|
||||
const { children } = props;
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.accessToken).toBe("test-token"));
|
||||
|
||||
@@ -163,8 +143,8 @@ describe("AuthProvider", () => {
|
||||
]);
|
||||
await act(() => result.current.logout());
|
||||
|
||||
expect(result.current.user).toBeNull();
|
||||
expect(result.current.accessToken).toBeNull();
|
||||
expect(result.current.user).toBeUndefined();
|
||||
expect(result.current.accessToken).toBeUndefined();
|
||||
expect(queryClient.getQueryData(statusKey)).toBeUndefined();
|
||||
expect(queryClient.getQueryData(portfolioKey)).toBeUndefined();
|
||||
expect(queryClient.getQueryData(bankCardsKey)).toBeUndefined();
|
||||
@@ -177,10 +157,7 @@ describe("AuthProvider", () => {
|
||||
])(
|
||||
"serializes late refresh (failure=%s, login=%s)",
|
||||
async (failure, loginAfterLogout) => {
|
||||
let release!: () => void;
|
||||
const pending = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
const { promise: pending, resolve: release } = Promise.withResolvers<void>();
|
||||
let calls = 0;
|
||||
const order: string[] = [];
|
||||
server.use(
|
||||
@@ -194,7 +171,7 @@ describe("AuthProvider", () => {
|
||||
? HttpResponse.error()
|
||||
: HttpResponse.json({
|
||||
accessToken: "a",
|
||||
user: { id: "a", email: "a@example.com" },
|
||||
user: { email: "a@example.com", id: "a" },
|
||||
});
|
||||
}),
|
||||
http.post("*/api/v1/auth/logout", () => {
|
||||
@@ -205,7 +182,7 @@ describe("AuthProvider", () => {
|
||||
order.push("login-cookie");
|
||||
return HttpResponse.json({
|
||||
accessToken: "b",
|
||||
user: { id: "b", email: "b@example.com" },
|
||||
user: { email: "b@example.com", id: "b" },
|
||||
});
|
||||
}),
|
||||
http.get("*/api/v1/brokerage-accounts", () =>
|
||||
@@ -213,22 +190,20 @@ describe("AuthProvider", () => {
|
||||
),
|
||||
);
|
||||
const queryClient = createTestQueryClient();
|
||||
const wrapper: FC<PropsWithChildren> = ({ children }) => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.ready).toBe(true));
|
||||
const oldRequest = apiClient
|
||||
.GET("/api/v1/brokerage-accounts")
|
||||
.catch(() => null);
|
||||
|
||||
// eslint-disable-next-line unicorn/prefer-await -- Attach rejection handling before releasing the deliberately pending query.
|
||||
.catch(() => {});
|
||||
await waitFor(() => expect(order).toContain("refresh-start"));
|
||||
let logout!: Promise<void>;
|
||||
act(() => {
|
||||
logout = result.current.logout();
|
||||
});
|
||||
await waitFor(() => expect(result.current.user).toBeNull());
|
||||
await waitFor(() => expect(result.current.user).toBeUndefined());
|
||||
let login!: Promise<void>;
|
||||
act(() => {
|
||||
login = loginAfterLogout
|
||||
@@ -239,8 +214,8 @@ describe("AuthProvider", () => {
|
||||
release();
|
||||
await Promise.all([oldRequest, logout, login]);
|
||||
});
|
||||
expect(result.current.user?.id ?? null).toBe(
|
||||
loginAfterLogout ? "b" : null,
|
||||
expect(result.current.user?.id ?? /* eslint-disable-line unicorn/no-null -- Exercise the explicit null returned by the production API/DOM contract. */ null).toBe(
|
||||
loginAfterLogout ? "b" : /* eslint-disable-line unicorn/no-null -- Exercise the explicit null returned by the production API/DOM contract. */ null,
|
||||
);
|
||||
expect(order).toEqual([
|
||||
"refresh-start",
|
||||
@@ -257,16 +232,12 @@ describe("AuthProvider", () => {
|
||||
http.post("*/api/v1/auth/refresh", () =>
|
||||
HttpResponse.json({
|
||||
accessToken: identity,
|
||||
user: { id: identity, email: `${identity}@example.com` },
|
||||
user: { email: `${identity}@example.com`, id: identity },
|
||||
}),
|
||||
),
|
||||
);
|
||||
const queryClient = createTestQueryClient();
|
||||
const wrapper: FC<PropsWithChildren> = ({ children }) => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.ready).toBe(true));
|
||||
queryClient.setQueryData(bankCardsKey, [{ id: "a-card" }]);
|
||||
@@ -274,14 +245,16 @@ describe("AuthProvider", () => {
|
||||
expect(queryClient.getQueryData(bankCardsKey)).toEqual([{ id: "a-card" }]);
|
||||
let release!: (value: string) => void;
|
||||
const pending = queryClient
|
||||
.fetchQuery({
|
||||
.query({
|
||||
queryFn: () => {
|
||||
const deferred = Promise.withResolvers<string>();
|
||||
release = deferred.resolve;
|
||||
return deferred.promise;
|
||||
},
|
||||
queryKey: ["pending-finance"],
|
||||
queryFn: () =>
|
||||
new Promise<string>((resolve) => {
|
||||
release = resolve;
|
||||
}),
|
||||
})
|
||||
.catch(() => null);
|
||||
// eslint-disable-next-line unicorn/prefer-await -- Attach rejection handling before releasing the deliberately pending query.
|
||||
.catch(() => {});
|
||||
identity = "b";
|
||||
await act(() => result.current.refresh());
|
||||
release("a-data");
|
||||
@@ -296,7 +269,7 @@ describe("AuthProvider", () => {
|
||||
http.post("*/api/v1/auth/refresh", () =>
|
||||
HttpResponse.json({
|
||||
accessToken: identity,
|
||||
user: { id: identity, email: `${identity}@example.com` },
|
||||
user: { email: `${identity}@example.com`, id: identity },
|
||||
}),
|
||||
),
|
||||
http.get("*/api/v1/bank-cards", () =>
|
||||
@@ -304,11 +277,7 @@ describe("AuthProvider", () => {
|
||||
),
|
||||
);
|
||||
const queryClient = createTestQueryClient();
|
||||
const wrapper: FC<PropsWithChildren> = (props) => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{props.children}</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
const wrapper: FC<PropsWithChildren> = createAuthWrapper(queryClient);
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
auth: useAuth(),
|
||||
|
||||
Reference in New Issue
Block a user