chore: migrate a bunch of tests to vitest (#21514)

This commit is contained in:
ケイラ
2026-01-15 12:38:29 -07:00
committed by GitHub
parent d25d95231f
commit d2d7c0ee40
25 changed files with 86 additions and 89 deletions
@@ -17,7 +17,7 @@ describe(PaginationWidgetBase.name, () => {
];
for (const props of sampleProps) {
const onPageChange = jest.fn();
const onPageChange = vi.fn();
const { unmount } = renderWithAuth(
<PaginationWidgetBase {...props} onPageChange={onPageChange} />,
);
@@ -39,7 +39,7 @@ describe(PaginationWidgetBase.name, () => {
];
for (const props of sampleProps) {
const onPageChange = jest.fn();
const onPageChange = vi.fn();
const { unmount } = renderWithAuth(
<PaginationWidgetBase {...props} onPageChange={onPageChange} />,
);
@@ -61,7 +61,7 @@ describe(PaginationWidgetBase.name, () => {
];
for (const props of sampleProps) {
const onPageChange = jest.fn();
const onPageChange = vi.fn();
const { unmount } = renderWithAuth(
<PaginationWidgetBase {...props} onPageChange={onPageChange} />,
);
@@ -22,13 +22,12 @@ import {
} from "./ProxyContext";
import type * as ProxyLatency from "./useProxyLatency";
// Mock useProxyLatency to use a hard-coded latency. 'jest.mock' must be called
// here and not inside a unit test.
jest.mock("contexts/useProxyLatency", () => ({
// Mock useProxyLatency to use a hard-coded latency.
vi.mock("contexts/useProxyLatency", () => ({
useProxyLatency: () => {
return {
proxyLatencies: hardCodedLatencies,
refetch: jest.fn(),
refetch: vi.fn(),
loaded: true,
};
},
@@ -48,9 +48,9 @@ const createAuthWrapper = (override: Partial<AuthContextValue>) => {
organizationIds: undefined,
signInError: undefined,
updateProfileError: undefined,
signOut: jest.fn(),
signIn: jest.fn(),
updateProfile: jest.fn(),
signOut: vi.fn(),
signIn: vi.fn(),
updateProfile: vi.fn(),
...override,
};
const Wrapper: FC<PropsWithChildren> = ({ children }) => {
@@ -66,7 +66,7 @@ const createAuthWrapper = (override: Partial<AuthContextValue>) => {
describe("useAuthenticated", () => {
it("throws an error if it is used outside of a context with user", () => {
jest.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(console, "error").mockImplementation(() => {});
expect(() => {
renderHook(() => useAuthenticated(), {
@@ -74,11 +74,11 @@ describe("useAuthenticated", () => {
});
}).toThrow("User is not authenticated.");
jest.restoreAllMocks();
vi.restoreAllMocks();
});
it("throws an error if it is used outside of a context with permissions", () => {
jest.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(console, "error").mockImplementation(() => {});
expect(() => {
renderHook(() => useAuthenticated(), {
@@ -86,7 +86,7 @@ describe("useAuthenticated", () => {
});
}).toThrow("Permissions are not available.");
jest.restoreAllMocks();
vi.restoreAllMocks();
});
it("returns auth context values for authenticated context", () => {
@@ -1,7 +1,7 @@
import { render, screen } from "@testing-library/react";
import { ClassicParameterFlowDeprecationWarning } from "./ClassicParameterFlowDeprecationWarning";
jest.mock("modules/navigation", () => ({
vi.mock("modules/navigation", () => ({
useLinks: () => () => "/mock-link",
linkToTemplate: () => "/mock-template-link",
}));
@@ -48,7 +48,7 @@ const renderPage = async ({ filter, page }: RenderPageOptions = {}) => {
describe("AuditPage", () => {
beforeEach(() => {
// Mocking the dayjs module within the createDayString file
const mock = jest.spyOn(CreateDayString, "createDayString");
const mock = vi.spyOn(CreateDayString, "createDayString");
mock.mockImplementation(() => "a minute ago");
// Mock the entitlements
@@ -62,7 +62,7 @@ describe("AuditPage", () => {
it("renders page 5", async () => {
// Given
const page = 5;
const getAuditLogsSpy = jest.spyOn(API, "getAuditLogs").mockResolvedValue({
const getAuditLogsSpy = vi.spyOn(API, "getAuditLogs").mockResolvedValue({
audit_logs: [MockAuditLog, MockAuditLog2],
count: 2,
});
@@ -82,7 +82,7 @@ describe("AuditPage", () => {
describe("Filtering", () => {
it("filters by URL", async () => {
const getAuditLogsSpy = jest
const getAuditLogsSpy = vi
.spyOn(API, "getAuditLogs")
.mockResolvedValue({ audit_logs: [MockAuditLog], count: 1 });
@@ -99,7 +99,7 @@ describe("AuditPage", () => {
it("resets page to 1 when filter is changed", async () => {
await renderPage({ page: 2 });
const getAuditLogsSpy = jest.spyOn(API, "getAuditLogs");
const getAuditLogsSpy = vi.spyOn(API, "getAuditLogs");
getAuditLogsSpy.mockClear();
const filterField = screen.getByLabelText("Filter");
@@ -47,7 +47,7 @@ const renderPage = async ({ filter, page }: RenderPageOptions = {}) => {
describe("ConnectionLogPage", () => {
beforeEach(() => {
// Mocking the dayjs module within the createDayString file
const mock = jest.spyOn(CreateDayString, "createDayString");
const mock = vi.spyOn(CreateDayString, "createDayString");
mock.mockImplementation(() => "a minute ago");
// Mock the entitlements
@@ -61,7 +61,7 @@ describe("ConnectionLogPage", () => {
it("renders page 5", async () => {
// Given
const page = 5;
const getConnectionLogsSpy = jest
const getConnectionLogsSpy = vi
.spyOn(API, "getConnectionLogs")
.mockResolvedValue({
connection_logs: [
@@ -90,7 +90,7 @@ describe("ConnectionLogPage", () => {
describe("Filtering", () => {
it("filters by URL", async () => {
const getConnectionLogsSpy = jest
const getConnectionLogsSpy = vi
.spyOn(API, "getConnectionLogs")
.mockResolvedValue({
connection_logs: [MockConnectedSSHConnectionLog],
@@ -110,7 +110,7 @@ describe("ConnectionLogPage", () => {
it("resets page to 1 when filter is changed", async () => {
await renderPage({ page: 2 });
const getConnectionLogsSpy = jest.spyOn(API, "getConnectionLogs");
const getConnectionLogsSpy = vi.spyOn(API, "getConnectionLogs");
getConnectionLogsSpy.mockClear();
const filterField = screen.getByLabelText("Filter");
@@ -16,7 +16,7 @@ import type { SlimRole } from "api/typesGenerated";
import { HttpResponse, http } from "msw";
import OrganizationMembersPage from "./OrganizationMembersPage";
jest.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(console, "error").mockImplementation(() => {});
beforeEach(() => {
server.use(
@@ -12,7 +12,7 @@ import { screen } from "@testing-library/react";
import { HttpResponse, http } from "msw";
import OrganizationRedirect from "./OrganizationRedirect";
jest.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(console, "error").mockImplementation(() => {});
const renderPage = async () => {
const { router } = renderWithOrganizationSettingsLayout(
@@ -124,7 +124,7 @@ describe("Setup Page", () => {
});
it("calls sendBeacon with telemetry", async () => {
const sendBeacon = jest.fn();
const sendBeacon = vi.fn();
Object.defineProperty(window.navigator, "sendBeacon", {
value: sendBeacon,
});
@@ -24,9 +24,10 @@ it("redirects from multi-org to single-org", async () => {
});
it("redirects from single-org to multi-org", async () => {
jest
.spyOn(API, "getOrganizations")
.mockResolvedValueOnce([M.MockDefaultOrganization, M.MockOrganization2]);
vi.spyOn(API, "getOrganizations").mockResolvedValueOnce([
M.MockDefaultOrganization,
M.MockOrganization2,
]);
const { router } = renderTemplateRedirectController(
`/templates/${M.MockTemplate.name}`,
@@ -110,29 +110,27 @@ const fillAndSubmitForm = async ({
await user.click(confirmButton);
};
// One problem with the waitFor function is that if no additional config options
// are passed in, it will hang indefinitely as it keeps retrying an assertion.
// Even if Jest runs out of time and kills the test, you won't get a good error
// message. Adding options to force test to give up before test timeout
// By default, waitFor will lock until it succeeds or the test times out, and
// won't give a very informative error if the test times out. Setting a timeout
// slightly shorter than the test timeout helps give a more informative error
// because Vitest will act as if something specific went wrong rather than
// vaguely handwaving about something taking too long.
function waitForWithCutoff(callback: () => void | Promise<void>) {
return waitFor(callback, {
// Defined to end 500ms before global cut-off time of 20s. Wanted to define
// this in terms of an exported constant from jest.config, but since Jest
// is CJS-based, that would've involved weird CJS-ESM interop issues
timeout: 19_500,
timeout: 4_500,
});
}
describe("TemplateSchedulePage", () => {
beforeEach(() => {
jest
.spyOn(API, "getEntitlements")
.mockResolvedValue(MockEntitlementsWithScheduling);
vi.spyOn(API, "getEntitlements").mockResolvedValue(
MockEntitlementsWithScheduling,
);
});
it("Calls the API when user fills in and submits a form", async () => {
await renderTemplateSchedulePage();
jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
vi.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
...MockTemplate,
...validFormValues,
});
@@ -146,7 +144,7 @@ describe("TemplateSchedulePage", () => {
test("default is converted to and from hours", async () => {
await renderTemplateSchedulePage();
jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
vi.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
...MockTemplate,
...validFormValues,
});
@@ -169,7 +167,7 @@ describe("TemplateSchedulePage", () => {
test("failure, dormancy, and dormancy auto-deletion converted to and from days", async () => {
await renderTemplateSchedulePage();
jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
vi.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
...MockTemplate,
...validFormValues,
});
@@ -25,7 +25,7 @@ const fillAndSubmitForm = async () => {
describe("AccountPage", () => {
describe("when it is a success", () => {
it("shows the success message", async () => {
jest.spyOn(API, "updateProfile").mockImplementationOnce((userId, data) =>
vi.spyOn(API, "updateProfile").mockImplementationOnce((userId, data) =>
Promise.resolve({
id: userId,
email: "user@coder.com",
@@ -53,7 +53,7 @@ describe("AccountPage", () => {
describe("when the username is already taken", () => {
it("shows an error", async () => {
jest.spyOn(API, "updateProfile").mockRejectedValueOnce(
vi.spyOn(API, "updateProfile").mockRejectedValueOnce(
mockApiError({
message: "Invalid profile",
validations: [
@@ -76,7 +76,7 @@ describe("AccountPage", () => {
describe("when it is an unknown error", () => {
it("shows a generic error message", async () => {
jest.spyOn(API, "updateProfile").mockRejectedValueOnce({
vi.spyOn(API, "updateProfile").mockRejectedValueOnce({
data: "unknown error",
});
@@ -9,7 +9,7 @@ describe("appearance page", () => {
it("does nothing when selecting current theme", async () => {
renderWithAuth(<AppearancePage />);
jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({
vi.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({
...MockUserOwner,
theme_preference: "dark",
terminal_font: "fira-code",
@@ -25,7 +25,7 @@ describe("appearance page", () => {
it("changes theme to light", async () => {
renderWithAuth(<AppearancePage />);
jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({
vi.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({
...MockUserOwner,
terminal_font: "ibm-plex-mono",
theme_preference: "light",
@@ -45,7 +45,7 @@ describe("appearance page", () => {
it("changes font to fira code", async () => {
renderWithAuth(<AppearancePage />);
jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({
vi.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({
...MockUserOwner,
terminal_font: "fira-code",
theme_preference: "dark",
@@ -66,8 +66,7 @@ describe("appearance page", () => {
renderWithAuth(<AppearancePage />);
// given
jest
.spyOn(API, "updateAppearanceSettings")
vi.spyOn(API, "updateAppearanceSettings")
.mockResolvedValueOnce({
...MockUserOwner,
terminal_font: "fira-code",
@@ -28,7 +28,7 @@ describe("SSH keys Page", () => {
const newUserSSHKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDSC/ouD/LqiT1Rd99vDv/MwUmqzJuinLTMTpk5kVy66";
jest.spyOn(API, "regenerateUserSSHKey").mockResolvedValueOnce({
vi.spyOn(API, "regenerateUserSSHKey").mockResolvedValueOnce({
...MockGitSSHKey,
public_key: newUserSSHKey,
});
@@ -57,7 +57,7 @@ describe("SSH keys Page", () => {
// Wait to the ssh be rendered on the screen
await screen.findByText(MockGitSSHKey.public_key);
jest.spyOn(API, "regenerateUserSSHKey").mockRejectedValueOnce(
vi.spyOn(API, "regenerateUserSSHKey").mockRejectedValueOnce(
mockApiError({
message: SSHKeysPageLanguage.regenerationError,
}),
@@ -50,7 +50,7 @@ const renderScheduleControls = async () => {
test("add 3 hours to deadline", async () => {
const user = userEvent.setup();
const updateDeadlineSpy = jest
const updateDeadlineSpy = vi
.spyOn(API, "putWorkspaceExtension")
.mockResolvedValue();
@@ -79,7 +79,7 @@ test("add 3 hours to deadline", async () => {
test("remove 2 hours to deadline", async () => {
const user = userEvent.setup();
const updateDeadlineSpy = jest
const updateDeadlineSpy = vi
.spyOn(API, "putWorkspaceExtension")
.mockResolvedValue();
@@ -108,7 +108,7 @@ test("remove 2 hours to deadline", async () => {
test("rollback to previous deadline on error", async () => {
const user = userEvent.setup();
const initialScheduleMessage = "Stop in 3 hours";
jest.spyOn(API, "putWorkspaceExtension").mockRejectedValue({});
vi.spyOn(API, "putWorkspaceExtension").mockRejectedValue({});
await renderScheduleControls();
@@ -127,7 +127,7 @@ test("rollback to previous deadline on error", async () => {
test("request is only sent once when clicking multiple times", async () => {
const user = userEvent.setup();
const updateDeadlineSpy = jest
const updateDeadlineSpy = vi
.spyOn(API, "putWorkspaceExtension")
.mockResolvedValue();
@@ -19,23 +19,23 @@ import WorkspaceParametersPage from "./WorkspaceParametersPage";
test("Submit the workspace settings page successfully", async () => {
// Mock the API calls that loads data
jest
.spyOn(API, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce(MockWorkspace);
jest.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce([
vi.spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValueOnce(
MockWorkspace,
);
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce([
MockTemplateVersionParameter1,
MockTemplateVersionParameter2,
// Immutable parameters
MockTemplateVersionParameter4,
]);
jest.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([
MockWorkspaceBuildParameter1,
MockWorkspaceBuildParameter2,
// Immutable value
MockWorkspaceBuildParameter4,
]);
// Mock the API calls that submit data
const postWorkspaceBuildSpy = jest
const postWorkspaceBuildSpy = vi
.spyOn(API, "postWorkspaceBuild")
.mockResolvedValue(MockWorkspaceBuild);
// Setup event and rendering
@@ -79,16 +79,16 @@ test("Submit the workspace settings page successfully", async () => {
test("Submit button is only enabled when changes are made", async () => {
// Mock the API calls that loads data
jest
.spyOn(API, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce(MockWorkspace);
jest.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce([
vi.spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValueOnce(
MockWorkspace,
);
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce([
MockTemplateVersionParameter1,
MockTemplateVersionParameter2,
// Immutable parameters
MockTemplateVersionParameter4,
]);
jest.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([
MockWorkspaceBuildParameter1,
MockWorkspaceBuildParameter2,
// Immutable value
@@ -270,7 +270,7 @@ const defaultFormProps: WorkspaceScheduleFormProps = {
describe("templateInheritance", () => {
it("disables the entire autostart feature appropriately", async () => {
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
const props = {
...defaultFormProps,
template: {
@@ -300,7 +300,7 @@ describe("templateInheritance", () => {
it("disables the autostart days of the week appropriately", async () => {
const enabledDayLabels = ["Sat", "Sun"];
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
const props = {
...defaultFormProps,
template: {
@@ -346,7 +346,7 @@ describe("templateInheritance", () => {
allow_user_autostop: false,
},
};
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
render(<WorkspaceScheduleForm {...props} />);
const autoStopToggle = await screen.findByLabelText("Enable Autostop", {
@@ -360,7 +360,7 @@ describe("templateInheritance", () => {
expect(ttlInput).toBeDisabled();
});
it("disables secondary autostart fields if main feature switch is toggled off", async () => {
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
render(
<WorkspaceScheduleForm
{...defaultFormProps}
@@ -384,7 +384,7 @@ describe("templateInheritance", () => {
}
});
it("disables secondary autostop fields if main feature switch is toggled off", async () => {
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
render(
<WorkspaceScheduleForm
{...defaultFormProps}
@@ -403,7 +403,7 @@ describe("templateInheritance", () => {
});
test("form should be enabled when both auto stop and auto start features are disabled, given that the template permits these actions", async () => {
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
render(
<WorkspaceScheduleForm
{...defaultFormProps}
@@ -430,7 +430,7 @@ test("form should be disabled when both auto stop and auto start features are di
allow_user_autostop: false,
},
};
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
vi.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
render(<WorkspaceScheduleForm {...props} />);
const submitButton = await screen.findByRole("button", {
@@ -10,13 +10,11 @@ import WorkspaceSettingsPage from "./WorkspaceSettingsPage";
test("Submit the workspace settings page successfully", async () => {
// Mock the API calls that loads data
jest
.spyOn(API, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce({ ...MockWorkspace });
vi.spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValueOnce({
...MockWorkspace,
});
// Mock the API calls that submit data
const patchWorkspaceSpy = jest
.spyOn(API, "patchWorkspace")
.mockResolvedValue();
const patchWorkspaceSpy = vi.spyOn(API, "patchWorkspace").mockResolvedValue();
// Setup event and rendering
const user = userEvent.setup();
renderWithWorkspaceSettingsLayout(<WorkspaceSettingsPage />, {
@@ -42,9 +40,10 @@ test("Submit the workspace settings page successfully", async () => {
test("Name field is disabled if renames are disabled", async () => {
// Mock the API calls that loads data
jest
.spyOn(API, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce({ ...MockWorkspace, allow_renames: false });
vi.spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValueOnce({
...MockWorkspace,
allow_renames: false,
});
renderWithWorkspaceSettingsLayout(<WorkspaceSettingsPage />, {
route: "/@test-user/test-workspace/settings",
path: "/:username/:workspace/settings",
@@ -12,7 +12,7 @@ interface TestType {
maxLengthOver: string;
}
const mockHandleChange = jest.fn();
const mockHandleChange = vi.fn();
const form = {
errors: {
@@ -43,12 +43,12 @@ const form = {
maxLengthOver: "a".repeat(33),
},
handleChange: mockHandleChange,
handleBlur: jest.fn(),
handleBlur: vi.fn(),
getFieldProps: (name: keyof TestType) => {
return {
name,
onBlur: jest.fn(),
onChange: jest.fn(),
onBlur: vi.fn(),
onChange: vi.fn(),
value: form.values[name] ?? "",
};
},
+2 -1
View File
@@ -2,5 +2,6 @@
globalThis.HTMLElement.prototype.hasPointerCapture = vi
.fn()
.mockReturnValue(false);
globalThis.HTMLElement.prototype.setPointerCapture = vi.fn();
globalThis.HTMLElement.prototype.releasePointerCapture = vi.fn();
globalThis.HTMLElement.prototype.scrollIntoView = vi.fn();
globalThis.HTMLElement.prototype.setPointerCapture = vi.fn();