-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(frontend): Utilize TanStack Query (#5096)
- Loading branch information
Showing
66 changed files
with
1,617 additions
and
1,294 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,153 @@ | ||
import { describe, it, test } from "vitest"; | ||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; | ||
import { createRemixStub } from "@remix-run/testing"; | ||
import { screen, waitFor, within } from "@testing-library/react"; | ||
import { renderWithProviders } from "test-utils"; | ||
import userEvent from "@testing-library/user-event"; | ||
import MainApp from "#/routes/_oh"; | ||
import * as CaptureConsent from "#/utils/handle-capture-consent"; | ||
import i18n from "#/i18n"; | ||
|
||
describe("frontend/routes/_oh", () => { | ||
describe("brand logo", () => { | ||
it.todo("should not do anything if the user is in the main screen"); | ||
it.todo( | ||
"should be clickable and redirect to the main screen if the user is not in the main screen", | ||
const RemixStub = createRemixStub([{ Component: MainApp, path: "/" }]); | ||
|
||
const { userIsAuthenticatedMock, settingsAreUpToDateMock } = vi.hoisted( | ||
() => ({ | ||
userIsAuthenticatedMock: vi.fn(), | ||
settingsAreUpToDateMock: vi.fn(), | ||
}), | ||
); | ||
|
||
beforeAll(() => { | ||
vi.mock("#/utils/user-is-authenticated", () => ({ | ||
userIsAuthenticated: userIsAuthenticatedMock.mockReturnValue(true), | ||
})); | ||
|
||
vi.mock("#/services/settings", async (importOriginal) => ({ | ||
...(await importOriginal<typeof import("#/services/settings")>()), | ||
settingsAreUpToDate: settingsAreUpToDateMock, | ||
})); | ||
}); | ||
|
||
afterEach(() => { | ||
vi.clearAllMocks(); | ||
localStorage.clear(); | ||
}); | ||
|
||
it("should render", async () => { | ||
renderWithProviders(<RemixStub />); | ||
await screen.findByTestId("root-layout"); | ||
}); | ||
|
||
it("should render the AI config modal if the user is authed", async () => { | ||
// Our mock return value is true by default | ||
renderWithProviders(<RemixStub />); | ||
await screen.findByTestId("ai-config-modal"); | ||
}); | ||
|
||
it("should render the AI config modal if settings are not up-to-date", async () => { | ||
settingsAreUpToDateMock.mockReturnValue(false); | ||
renderWithProviders(<RemixStub />); | ||
|
||
await screen.findByTestId("ai-config-modal"); | ||
}); | ||
|
||
it("should not render the AI config modal if the settings are up-to-date", async () => { | ||
settingsAreUpToDateMock.mockReturnValue(true); | ||
renderWithProviders(<RemixStub />); | ||
|
||
await waitFor(() => { | ||
expect(screen.queryByTestId("ai-config-modal")).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it("should capture the user's consent", async () => { | ||
const user = userEvent.setup(); | ||
const handleCaptureConsentSpy = vi.spyOn( | ||
CaptureConsent, | ||
"handleCaptureConsent", | ||
); | ||
|
||
renderWithProviders(<RemixStub />); | ||
|
||
// The user has not consented to tracking | ||
const consentForm = await screen.findByTestId("user-capture-consent-form"); | ||
expect(handleCaptureConsentSpy).not.toHaveBeenCalled(); | ||
expect(localStorage.getItem("analytics-consent")).toBeNull(); | ||
|
||
const submitButton = within(consentForm).getByRole("button", { | ||
name: /confirm preferences/i, | ||
}); | ||
await user.click(submitButton); | ||
|
||
// The user has now consented to tracking | ||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(true); | ||
expect(localStorage.getItem("analytics-consent")).toBe("true"); | ||
expect( | ||
screen.queryByTestId("user-capture-consent-form"), | ||
).not.toBeInTheDocument(); | ||
}); | ||
|
||
describe("user menu", () => { | ||
it.todo("should open the user menu when clicked"); | ||
it("should not render the user consent form if the user has already made a decision", async () => { | ||
localStorage.setItem("analytics-consent", "true"); | ||
renderWithProviders(<RemixStub />); | ||
|
||
describe("logged out", () => { | ||
it.todo("should display a placeholder"); | ||
test.todo("the logout option in the user menu should be disabled"); | ||
await waitFor(() => { | ||
expect( | ||
screen.queryByTestId("user-capture-consent-form"), | ||
).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it("should render a new project button if a token is set", async () => { | ||
localStorage.setItem("token", "test-token"); | ||
const { rerender } = renderWithProviders(<RemixStub />); | ||
|
||
describe("logged in", () => { | ||
it.todo("should display the user's avatar"); | ||
it.todo("should log the user out when the logout option is clicked"); | ||
await screen.findByTestId("new-project-button"); | ||
|
||
localStorage.removeItem("token"); | ||
rerender(<RemixStub />); | ||
|
||
await waitFor(() => { | ||
expect( | ||
screen.queryByTestId("new-project-button"), | ||
).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
describe("config", () => { | ||
it.todo("should open the config modal when clicked"); | ||
it.todo( | ||
"should not save the config and close the config modal when the close button is clicked", | ||
); | ||
it.todo( | ||
"should save the config when the save button is clicked and close the modal", | ||
); | ||
it.todo("should warn the user about saving the config when in /app"); | ||
// TODO: Move to e2e tests | ||
it.skip("should update the i18n language when the language settings change", async () => { | ||
const changeLanguageSpy = vi.spyOn(i18n, "changeLanguage"); | ||
const { rerender } = renderWithProviders(<RemixStub />); | ||
|
||
// The default language is English | ||
expect(changeLanguageSpy).toHaveBeenCalledWith("en"); | ||
|
||
localStorage.setItem("LANGUAGE", "es"); | ||
|
||
rerender(<RemixStub />); | ||
expect(changeLanguageSpy).toHaveBeenCalledWith("es"); | ||
|
||
rerender(<RemixStub />); | ||
// The language has not changed, so the spy should not have been called again | ||
expect(changeLanguageSpy).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
// FIXME: logoutCleanup has been replaced with a hook | ||
it.skip("should call logoutCleanup after a logout", async () => { | ||
const user = userEvent.setup(); | ||
localStorage.setItem("ghToken", "test-token"); | ||
|
||
// const logoutCleanupSpy = vi.spyOn(LogoutCleanup, "logoutCleanup"); | ||
renderWithProviders(<RemixStub />); | ||
|
||
const userActions = await screen.findByTestId("user-actions"); | ||
const userAvatar = within(userActions).getByTestId("user-avatar"); | ||
await user.click(userAvatar); | ||
|
||
const logout = within(userActions).getByRole("button", { name: /logout/i }); | ||
await user.click(logout); | ||
|
||
// expect(logoutCleanupSpy).toHaveBeenCalled(); | ||
expect(localStorage.getItem("ghToken")).toBeNull(); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
13 changes: 13 additions & 0 deletions
13
frontend/__tests__/utils/extract-next-page-from-link.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { expect, test } from "vitest"; | ||
import { extractNextPageFromLink } from "#/utils/extract-next-page-from-link"; | ||
|
||
test("extractNextPageFromLink", () => { | ||
const link = `<https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=4>; rel="next", <https://api.github.com/repositories/1300192/issues?page=515>; rel="last", <https://api.github.com/repositories/1300192/issues?page=1>; rel="first"`; | ||
expect(extractNextPageFromLink(link)).toBe(4); | ||
|
||
const noNextLink = `<https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=1>; rel="first"`; | ||
expect(extractNextPageFromLink(noNextLink)).toBeNull(); | ||
|
||
const extra = `<https://api.github.com/user/repos?sort=pushed&page=2&per_page=3>; rel="next", <https://api.github.com/user/repos?sort=pushed&page=22&per_page=3>; rel="last"`; | ||
expect(extractNextPageFromLink(extra)).toBe(2); | ||
}); |
Oops, something went wrong.