mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
chore: continue vitest test migrations (#21639)
This commit is contained in:
+1
-8
@@ -32,14 +32,7 @@ module.exports = {
|
||||
customExportConditions: [""],
|
||||
},
|
||||
testRegex: "(/__tests__/.*|(\\.|/)(jest))\\.tsx?$",
|
||||
testPathIgnorePatterns: [
|
||||
"/node_modules/",
|
||||
"/e2e/",
|
||||
// TODO: This test is timing out after upgrade a few Jest dependencies
|
||||
// and I was not able to figure out why. When running it specifically, I
|
||||
// can see many act warnings that may can help us to find the issue.
|
||||
"/usePaginatedQuery.test.ts",
|
||||
],
|
||||
testPathIgnorePatterns: ["/node_modules/", "/e2e/"],
|
||||
transformIgnorePatterns: [],
|
||||
moduleDirectories: ["node_modules", "<rootDir>/src"],
|
||||
moduleNameMapper: {
|
||||
|
||||
@@ -21,9 +21,9 @@ describe("api.ts", () => {
|
||||
session_token: "abc_123_test",
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(axiosInstance, "post")
|
||||
.mockResolvedValueOnce({ data: loginResponse });
|
||||
vi.spyOn(axiosInstance, "post").mockResolvedValueOnce({
|
||||
data: loginResponse,
|
||||
});
|
||||
|
||||
// when
|
||||
const result = await API.login("test", "123");
|
||||
@@ -41,7 +41,7 @@ describe("api.ts", () => {
|
||||
message: "Validation failed",
|
||||
errors: [{ field: "email", code: "email" }],
|
||||
};
|
||||
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||
const axiosMockPost = vi.fn().mockImplementationOnce(() => {
|
||||
return Promise.reject(expectedError);
|
||||
});
|
||||
axiosInstance.post = axiosMockPost;
|
||||
@@ -57,7 +57,7 @@ describe("api.ts", () => {
|
||||
describe("logout", () => {
|
||||
it("should return without erroring", async () => {
|
||||
// given
|
||||
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||
const axiosMockPost = vi.fn().mockImplementationOnce(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
axiosInstance.post = axiosMockPost;
|
||||
@@ -76,7 +76,7 @@ describe("api.ts", () => {
|
||||
const expectedError = {
|
||||
message: "Failed to logout.",
|
||||
};
|
||||
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||
const axiosMockPost = vi.fn().mockImplementationOnce(() => {
|
||||
return Promise.reject(expectedError);
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ describe("api.ts", () => {
|
||||
const apiKeyResponse: TypesGen.GenerateAPIKeyResponse = {
|
||||
key: "abc_123_test",
|
||||
};
|
||||
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||
const axiosMockPost = vi.fn().mockImplementationOnce(() => {
|
||||
return Promise.resolve({ data: apiKeyResponse });
|
||||
});
|
||||
|
||||
@@ -117,7 +117,7 @@ describe("api.ts", () => {
|
||||
const expectedError = {
|
||||
message: "No Cookie!",
|
||||
};
|
||||
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||
const axiosMockPost = vi.fn().mockImplementationOnce(() => {
|
||||
return Promise.reject(expectedError);
|
||||
});
|
||||
|
||||
@@ -175,16 +175,16 @@ describe("api.ts", () => {
|
||||
describe("update", () => {
|
||||
describe("given a running workspace", () => {
|
||||
it("stops with current version before starting with the latest version", async () => {
|
||||
jest.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
transition: "stop",
|
||||
});
|
||||
jest.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
template_version_id: MockTemplateVersion2.id,
|
||||
transition: "start",
|
||||
});
|
||||
jest.spyOn(API, "getTemplate").mockResolvedValueOnce({
|
||||
vi.spyOn(API, "getTemplate").mockResolvedValueOnce({
|
||||
...MockTemplate,
|
||||
active_version_id: MockTemplateVersion2.id,
|
||||
});
|
||||
@@ -201,17 +201,15 @@ describe("api.ts", () => {
|
||||
});
|
||||
|
||||
it("fails when having missing parameters", async () => {
|
||||
jest
|
||||
.spyOn(API, "postWorkspaceBuild")
|
||||
.mockResolvedValue(MockWorkspaceBuild);
|
||||
jest.spyOn(API, "getTemplate").mockResolvedValue(MockTemplate);
|
||||
jest.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValue([]);
|
||||
jest
|
||||
.spyOn(API, "getTemplateVersionRichParameters")
|
||||
.mockResolvedValue([
|
||||
MockTemplateVersionParameter1,
|
||||
{ ...MockTemplateVersionParameter2, mutable: false },
|
||||
]);
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValue(
|
||||
MockWorkspaceBuild,
|
||||
);
|
||||
vi.spyOn(API, "getTemplate").mockResolvedValue(MockTemplate);
|
||||
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValue([]);
|
||||
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValue([
|
||||
MockTemplateVersionParameter1,
|
||||
{ ...MockTemplateVersionParameter2, mutable: false },
|
||||
]);
|
||||
|
||||
let error = new Error();
|
||||
try {
|
||||
@@ -229,20 +227,20 @@ describe("api.ts", () => {
|
||||
});
|
||||
|
||||
it("creates a build with no parameters if it is already filled", async () => {
|
||||
jest.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
transition: "stop",
|
||||
});
|
||||
jest.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
template_version_id: MockTemplateVersion2.id,
|
||||
transition: "start",
|
||||
});
|
||||
jest.spyOn(API, "getTemplate").mockResolvedValueOnce(MockTemplate);
|
||||
jest
|
||||
.spyOn(API, "getWorkspaceBuildParameters")
|
||||
.mockResolvedValue([MockWorkspaceBuildParameter1]);
|
||||
jest.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValue([
|
||||
vi.spyOn(API, "getTemplate").mockResolvedValueOnce(MockTemplate);
|
||||
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValue([
|
||||
MockWorkspaceBuildParameter1,
|
||||
]);
|
||||
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValue([
|
||||
{
|
||||
...MockTemplateVersionParameter1,
|
||||
required: true,
|
||||
@@ -263,10 +261,10 @@ describe("api.ts", () => {
|
||||
});
|
||||
describe("given a stopped workspace", () => {
|
||||
it("creates a build with start and the latest template", async () => {
|
||||
jest
|
||||
.spyOn(API, "postWorkspaceBuild")
|
||||
.mockResolvedValueOnce(MockWorkspaceBuild);
|
||||
jest.spyOn(API, "getTemplate").mockResolvedValueOnce({
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce(
|
||||
MockWorkspaceBuild,
|
||||
);
|
||||
vi.spyOn(API, "getTemplate").mockResolvedValueOnce({
|
||||
...MockTemplate,
|
||||
active_version_id: MockTemplateVersion2.id,
|
||||
});
|
||||
@@ -1,14 +1,14 @@
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { useDebouncedFunction, useDebouncedValue } from "./debounce";
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(global, "setTimeout");
|
||||
vi.useFakeTimers();
|
||||
vi.spyOn(global, "setTimeout");
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
jest.clearAllMocks();
|
||||
vi.useRealTimers();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe(useDebouncedValue.name, () => {
|
||||
@@ -62,7 +62,7 @@ describe(useDebouncedValue.name, () => {
|
||||
}, i * 100);
|
||||
}
|
||||
|
||||
await jest.advanceTimersByTimeAsync(time - 100);
|
||||
await vi.advanceTimersByTimeAsync(time - 100);
|
||||
expect(result.current).toEqual(0);
|
||||
});
|
||||
|
||||
@@ -74,8 +74,13 @@ describe(useDebouncedValue.name, () => {
|
||||
expect(result.current).toEqual(false);
|
||||
|
||||
rerender({ value: !initialValue, time });
|
||||
await jest.runAllTimersAsync();
|
||||
await waitFor(() => expect(result.current).toEqual(true));
|
||||
|
||||
// Waiting until the debounce time has elapsed will trigger a state update.
|
||||
await act(async () => {
|
||||
await vi.runAllTimersAsync();
|
||||
});
|
||||
|
||||
expect(result.current).toEqual(true);
|
||||
});
|
||||
|
||||
// Very important that we not do any async logic for this test
|
||||
@@ -122,7 +127,7 @@ describe(`${useDebouncedFunction.name}`, () => {
|
||||
-42,
|
||||
];
|
||||
|
||||
const dummyFunction = jest.fn();
|
||||
const dummyFunction = vi.fn();
|
||||
for (const input of invalidInputs) {
|
||||
expect(() => {
|
||||
renderDebouncedFunction(dummyFunction, input);
|
||||
@@ -136,12 +141,12 @@ describe(`${useDebouncedFunction.name}`, () => {
|
||||
describe("hook", () => {
|
||||
it("Should provide stable function references across re-renders", () => {
|
||||
const time = 5000;
|
||||
const { result, rerender } = renderDebouncedFunction(jest.fn(), time);
|
||||
const { result, rerender } = renderDebouncedFunction(vi.fn(), time);
|
||||
|
||||
const { debounced: oldDebounced, cancelDebounce: oldCancel } =
|
||||
result.current;
|
||||
|
||||
rerender({ callback: jest.fn(), time });
|
||||
rerender({ callback: vi.fn(), time });
|
||||
const { debounced: newDebounced, cancelDebounce: newCancel } =
|
||||
result.current;
|
||||
|
||||
@@ -151,43 +156,43 @@ describe(`${useDebouncedFunction.name}`, () => {
|
||||
|
||||
it("Resets any pending debounces if the timer argument changes", async () => {
|
||||
const time = 5000;
|
||||
const mockCallback = jest.fn();
|
||||
const mockCallback = vi.fn();
|
||||
const { result, rerender } = renderDebouncedFunction(mockCallback, time);
|
||||
|
||||
result.current.debounced();
|
||||
rerender({ callback: mockCallback, time: time + 1 });
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
await vi.runAllTimersAsync();
|
||||
expect(mockCallback).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("debounced function", () => {
|
||||
it("Resolve the debounce after specified milliseconds pass with no other calls", async () => {
|
||||
const mockCallback = jest.fn();
|
||||
const mockCallback = vi.fn();
|
||||
const { result } = renderDebouncedFunction(mockCallback, 100);
|
||||
result.current.debounced();
|
||||
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
expect(mockCallback).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("Always uses the most recent callback argument passed in (even if it switches while a debounce is queued)", async () => {
|
||||
const mockCallback1 = jest.fn();
|
||||
const mockCallback2 = jest.fn();
|
||||
const mockCallback1 = vi.fn();
|
||||
const mockCallback2 = vi.fn();
|
||||
const time = 500;
|
||||
|
||||
const { result, rerender } = renderDebouncedFunction(mockCallback1, time);
|
||||
result.current.debounced();
|
||||
rerender({ callback: mockCallback2, time });
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
await vi.runAllTimersAsync();
|
||||
expect(mockCallback1).not.toBeCalled();
|
||||
expect(mockCallback2).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("Should reset the debounce timer with repeated calls to the method", async () => {
|
||||
const mockCallback = jest.fn();
|
||||
const mockCallback = vi.fn();
|
||||
const { result } = renderDebouncedFunction(mockCallback, 2000);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
@@ -196,20 +201,20 @@ describe(`${useDebouncedFunction.name}`, () => {
|
||||
}, i * 100);
|
||||
}
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
await vi.runAllTimersAsync();
|
||||
expect(mockCallback).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cancelDebounce function", () => {
|
||||
it("Should be able to cancel a pending debounce", async () => {
|
||||
const mockCallback = jest.fn();
|
||||
const mockCallback = vi.fn();
|
||||
const { result } = renderDebouncedFunction(mockCallback, 2000);
|
||||
|
||||
result.current.debounced();
|
||||
result.current.cancelDebounce();
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
await vi.runAllTimersAsync();
|
||||
expect(mockCallback).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -43,10 +43,12 @@ export function useDebouncedFunction<
|
||||
);
|
||||
}
|
||||
|
||||
const timeoutIdRef = useRef<number | undefined>(undefined);
|
||||
const timeoutIdRef = useRef<ReturnType<typeof setTimeout> | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const cancelDebounce = useCallback(() => {
|
||||
if (timeoutIdRef.current !== undefined) {
|
||||
window.clearTimeout(timeoutIdRef.current);
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
}
|
||||
|
||||
timeoutIdRef.current = undefined;
|
||||
@@ -70,7 +72,7 @@ export function useDebouncedFunction<
|
||||
(...args: Args): void => {
|
||||
cancelDebounce();
|
||||
|
||||
timeoutIdRef.current = window.setTimeout(
|
||||
timeoutIdRef.current = setTimeout(
|
||||
() => void callbackRef.current(...args),
|
||||
debounceTimeRef.current,
|
||||
);
|
||||
@@ -105,10 +107,10 @@ export function useDebouncedValue<T>(value: T, debounceTimeoutMs: number): T {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, debounceTimeoutMs);
|
||||
return () => window.clearTimeout(timeoutId);
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [value, debounceTimeoutMs]);
|
||||
|
||||
return debouncedValue;
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ describe(useEmbeddedMetadata.name, () => {
|
||||
const cleanupTags = seedInitialMetadata(key);
|
||||
const { result: reactResult, manager } = renderMetadataHook(key);
|
||||
|
||||
const nonReactSubscriber = jest.fn();
|
||||
const nonReactSubscriber = vi.fn();
|
||||
manager.subscribe(nonReactSubscriber);
|
||||
|
||||
const expectedUpdate1: RuntimeHtmlMetadata = {
|
||||
+73
-76
@@ -1,9 +1,3 @@
|
||||
// TODO: This test is timing out after upgrade a few Jest dependencies
|
||||
// and I was not able to figure out why. When running it specifically, I
|
||||
// can see many act warnings that may can help us to find the issue.
|
||||
// (Note: This comment was originally written by Bruno, and was relocated by
|
||||
// me. If you go poking at `git blame`, disabling these tests was not my idea.
|
||||
|
||||
import { renderHookWithAuth } from "testHelpers/hooks";
|
||||
import { waitFor } from "@testing-library/react";
|
||||
import {
|
||||
@@ -12,13 +6,8 @@ import {
|
||||
usePaginatedQuery,
|
||||
} from "./usePaginatedQuery";
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
jest.clearAllMocks();
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function render<
|
||||
@@ -39,16 +28,12 @@ function render<
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* There are a lot of test cases in this file. Scoping mocking to inner describe
|
||||
* function calls to limit the cognitive load of maintaining all this stuff
|
||||
*/
|
||||
describe.skip(usePaginatedQuery.name, () => {
|
||||
describe(usePaginatedQuery.name, () => {
|
||||
describe("queryPayload method", () => {
|
||||
const mockQueryFn = jest.fn(() => Promise.resolve({ count: 0 }));
|
||||
const mockQueryFn = vi.fn(() => Promise.resolve({ count: 0 }));
|
||||
|
||||
it("Passes along an undefined payload if queryPayload is not used", async () => {
|
||||
const mockQueryKey = jest.fn(() => ["mockQuery"]);
|
||||
const mockQueryKey = vi.fn(() => ["mockQuery"]);
|
||||
|
||||
await render({
|
||||
queryKey: mockQueryKey,
|
||||
@@ -64,7 +49,7 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
});
|
||||
|
||||
it("Passes along type-safe payload if queryPayload is provided", async () => {
|
||||
const mockQueryKey = jest.fn(({ payload }) => {
|
||||
const mockQueryKey = vi.fn(({ payload }) => {
|
||||
return ["mockQuery", payload];
|
||||
});
|
||||
|
||||
@@ -85,8 +70,8 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
});
|
||||
|
||||
describe("Querying for current page", () => {
|
||||
const mockQueryKey = jest.fn(() => ["mock"]);
|
||||
const mockQueryFn = jest.fn(() => Promise.resolve({ count: 50 }));
|
||||
const mockQueryKey = vi.fn(() => ["mock"]);
|
||||
const mockQueryFn = vi.fn(() => Promise.resolve({ count: 50 }));
|
||||
|
||||
it("Parses page number if it exists in URL params", async () => {
|
||||
const pageNumbers = [1, 2, 7, 39, 743];
|
||||
@@ -113,7 +98,7 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
});
|
||||
|
||||
describe("Prefetching", () => {
|
||||
const mockQueryKey = jest.fn(({ pageNumber }) => ["query", pageNumber]);
|
||||
const mockQueryKey = vi.fn(({ pageNumber }) => ["query", pageNumber]);
|
||||
|
||||
type Context = { pageNumber: number; limit: number };
|
||||
const mockQueryFnImplementation = ({ pageNumber, limit }: Context) => {
|
||||
@@ -134,7 +119,7 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
) => {
|
||||
// Have to reinitialize mock function every call to avoid false positives
|
||||
// from shared mutable tracking state
|
||||
const mockQueryFn = jest.fn(mockQueryFnImplementation);
|
||||
const mockQueryFn = vi.fn(mockQueryFnImplementation);
|
||||
const { result } = await render(
|
||||
{ queryKey: mockQueryKey, queryFn: mockQueryFn },
|
||||
`/?page=${startingPage}`,
|
||||
@@ -143,16 +128,12 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
const pageMatcher = expect.objectContaining({ pageNumber: targetPage });
|
||||
if (shouldMatch) {
|
||||
await waitFor(() => expect(result.current.totalRecords).toBeDefined());
|
||||
await waitFor(() => expect(mockQueryFn).toBeCalledWith(pageMatcher));
|
||||
await waitFor(() =>
|
||||
expect(mockQueryFn).toHaveBeenCalledWith(pageMatcher),
|
||||
);
|
||||
} else {
|
||||
// Can't use waitFor to test this, because the expect call will
|
||||
// immediately succeed for the not case, even though queryFn needs to be
|
||||
// called async via React Query
|
||||
setTimeout(() => {
|
||||
expect(mockQueryFn).not.toBeCalledWith(pageMatcher);
|
||||
}, 1000);
|
||||
|
||||
jest.runAllTimers();
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
expect(mockQueryFn).not.toHaveBeenCalledWith(pageMatcher);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -175,7 +156,7 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
|
||||
it("Reuses the same queryKey and queryFn methods for the current page and all prefetching (on a given render)", async () => {
|
||||
const startPage = 2;
|
||||
const mockQueryFn = jest.fn(mockQueryFnImplementation);
|
||||
const mockQueryFn = vi.fn(mockQueryFnImplementation);
|
||||
|
||||
await render(
|
||||
{ queryKey: mockQueryKey, queryFn: mockQueryFn },
|
||||
@@ -183,26 +164,34 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
);
|
||||
|
||||
const currentMatcher = expect.objectContaining({ pageNumber: startPage });
|
||||
expect(mockQueryKey).toBeCalledWith(currentMatcher);
|
||||
expect(mockQueryFn).toBeCalledWith(currentMatcher);
|
||||
expect(mockQueryKey).toHaveBeenCalledWith(currentMatcher);
|
||||
expect(mockQueryFn).toHaveBeenCalledWith(currentMatcher);
|
||||
|
||||
const prevPageMatcher = expect.objectContaining({
|
||||
pageNumber: startPage - 1,
|
||||
});
|
||||
await waitFor(() => expect(mockQueryKey).toBeCalledWith(prevPageMatcher));
|
||||
await waitFor(() => expect(mockQueryFn).toBeCalledWith(prevPageMatcher));
|
||||
await waitFor(() =>
|
||||
expect(mockQueryKey).toHaveBeenCalledWith(prevPageMatcher),
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(mockQueryFn).toHaveBeenCalledWith(prevPageMatcher),
|
||||
);
|
||||
|
||||
const nextPageMatcher = expect.objectContaining({
|
||||
pageNumber: startPage + 1,
|
||||
});
|
||||
await waitFor(() => expect(mockQueryKey).toBeCalledWith(nextPageMatcher));
|
||||
await waitFor(() => expect(mockQueryFn).toBeCalledWith(nextPageMatcher));
|
||||
await waitFor(() =>
|
||||
expect(mockQueryKey).toHaveBeenCalledWith(nextPageMatcher),
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(mockQueryFn).toHaveBeenCalledWith(nextPageMatcher),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Safety nets/redirects for invalid pages", () => {
|
||||
const mockQueryKey = jest.fn(() => ["mock"]);
|
||||
const mockQueryFn = jest.fn(({ pageNumber, limit }) =>
|
||||
const mockQueryKey = vi.fn(() => ["mock"]);
|
||||
const mockQueryFn = vi.fn(({ pageNumber, limit }) =>
|
||||
Promise.resolve({
|
||||
data: new Array(limit).fill(pageNumber),
|
||||
count: 100,
|
||||
@@ -244,7 +233,7 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
page: "1000",
|
||||
});
|
||||
|
||||
const onInvalidPageChange = jest.fn();
|
||||
const onInvalidPageChange = vi.fn();
|
||||
await render({
|
||||
onInvalidPageChange,
|
||||
queryKey: mockQueryKey,
|
||||
@@ -270,8 +259,8 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
});
|
||||
|
||||
describe("Passing in searchParams property", () => {
|
||||
const mockQueryKey = jest.fn(() => ["mock"]);
|
||||
const mockQueryFn = jest.fn(({ pageNumber, limit }) =>
|
||||
const mockQueryKey = vi.fn(() => ["mock"]);
|
||||
const mockQueryFn = vi.fn(({ pageNumber, limit }) =>
|
||||
Promise.resolve({
|
||||
data: new Array(limit).fill(pageNumber),
|
||||
count: 100,
|
||||
@@ -309,25 +298,19 @@ describe.skip(usePaginatedQuery.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(`${usePaginatedQuery.name} - Returned properties`, () => {
|
||||
describe(`${usePaginatedQuery.name} - Returned properties`, () => {
|
||||
describe("Page change methods", () => {
|
||||
const mockQueryKey = jest.fn(() => ["mock"]);
|
||||
const mockQueryKey = vi.fn(() => ["mock"]);
|
||||
|
||||
const mockQueryFn = jest.fn(({ pageNumber, limit }) => {
|
||||
type Data = PaginatedData & { data: readonly number[] };
|
||||
|
||||
return new Promise<Data>((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
data: new Array(limit).fill(pageNumber),
|
||||
count: 100,
|
||||
});
|
||||
}, 10_000);
|
||||
const mockQueryFn = vi.fn(({ pageNumber, limit }) => {
|
||||
return Promise.resolve({
|
||||
data: new Array(limit).fill(pageNumber),
|
||||
count: 100,
|
||||
});
|
||||
});
|
||||
|
||||
test("goToFirstPage always succeeds regardless of fetch status", async () => {
|
||||
const queryFns = [mockQueryFn, jest.fn(() => Promise.reject("Too bad"))];
|
||||
const queryFns = [mockQueryFn, vi.fn(() => Promise.reject("Too bad"))];
|
||||
|
||||
for (const queryFn of queryFns) {
|
||||
const { result, unmount } = await render(
|
||||
@@ -351,14 +334,22 @@ describe.skip(`${usePaginatedQuery.name} - Returned properties`, () => {
|
||||
"/?page=1",
|
||||
);
|
||||
|
||||
expect(result.current.hasNextPage).toBe(false);
|
||||
result.current.goToNextPage();
|
||||
expect(result.current.currentPage).toBe(1);
|
||||
// Wait for the query to complete and check we have next pages
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
expect(result.current.hasNextPage).toBe(true);
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
await waitFor(() => expect(result.current.hasNextPage).toBe(true));
|
||||
// Can go to next page when hasNextPage is true
|
||||
result.current.goToNextPage();
|
||||
await waitFor(() => expect(result.current.currentPage).toBe(2));
|
||||
|
||||
// Navigate to last page (page 4, since we have 100 items with 25 per page)
|
||||
result.current.onPageChange(4);
|
||||
await waitFor(() => expect(result.current.currentPage).toBe(4));
|
||||
|
||||
// Now hasNextPage should be false and goToNextPage should not change the page
|
||||
expect(result.current.hasNextPage).toBe(false);
|
||||
result.current.goToNextPage();
|
||||
expect(result.current.currentPage).toBe(4);
|
||||
});
|
||||
|
||||
test("goToPreviousPage works only if hasPreviousPage is true", async () => {
|
||||
@@ -370,14 +361,22 @@ describe.skip(`${usePaginatedQuery.name} - Returned properties`, () => {
|
||||
"/?page=3",
|
||||
);
|
||||
|
||||
expect(result.current.hasPreviousPage).toBe(false);
|
||||
result.current.goToPreviousPage();
|
||||
expect(result.current.currentPage).toBe(3);
|
||||
// Wait for the query to complete and check we have previous pages
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
expect(result.current.hasPreviousPage).toBe(true);
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
await waitFor(() => expect(result.current.hasPreviousPage).toBe(true));
|
||||
// Can go to previous page when hasPreviousPage is true
|
||||
result.current.goToPreviousPage();
|
||||
await waitFor(() => expect(result.current.currentPage).toBe(2));
|
||||
|
||||
// Navigate to first page
|
||||
result.current.goToFirstPage();
|
||||
await waitFor(() => expect(result.current.currentPage).toBe(1));
|
||||
|
||||
// Now hasPreviousPage should be false and goToPreviousPage should not change the page
|
||||
expect(result.current.hasPreviousPage).toBe(false);
|
||||
result.current.goToPreviousPage();
|
||||
expect(result.current.currentPage).toBe(1);
|
||||
});
|
||||
|
||||
test("onPageChange accounts for floats and truncates numeric values before navigating", async () => {
|
||||
@@ -386,7 +385,7 @@ describe.skip(`${usePaginatedQuery.name} - Returned properties`, () => {
|
||||
queryFn: mockQueryFn,
|
||||
});
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
// Wait for the initial query to complete
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
result.current.onPageChange(2.5);
|
||||
|
||||
@@ -399,18 +398,16 @@ describe.skip(`${usePaginatedQuery.name} - Returned properties`, () => {
|
||||
queryFn: mockQueryFn,
|
||||
});
|
||||
|
||||
await jest.runAllTimersAsync();
|
||||
// Wait for the initial query to complete
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
|
||||
result.current.onPageChange(Number.NaN);
|
||||
result.current.onPageChange(Number.POSITIVE_INFINITY);
|
||||
result.current.onPageChange(Number.NEGATIVE_INFINITY);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(result.current.currentPage).toBe(1);
|
||||
}, 1000);
|
||||
|
||||
jest.runAllTimers();
|
||||
// Give it a moment to ensure no navigation happens
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
expect(result.current.currentPage).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user