diff --git a/src/components/AppBar/tests/ProjectButtons.test.tsx b/src/components/AppBar/tests/ProjectButtons.test.tsx index b332c90324..c8de51657e 100644 --- a/src/components/AppBar/tests/ProjectButtons.test.tsx +++ b/src/components/AppBar/tests/ProjectButtons.test.tsx @@ -18,13 +18,15 @@ import { Goal, GoalStatus } from "types/goals"; import { Path } from "types/path"; import { themeColors } from "types/theme"; +jest.mock("react-router-dom", () => ({ + useNavigate: jest.fn(), +})); + jest.mock("backend", () => ({ hasPermission: (perm: Permission) => mockHasPermission(perm), isSiteAdmin: () => mockIsSiteAdmin(), })); -jest.mock("react-router-dom", () => ({ - useNavigate: jest.fn(), -})); +jest.mock("components/Project/ProjectActions", () => ({})); const mockHasPermission = jest.fn(); const mockIsSiteAdmin = jest.fn(); diff --git a/src/components/AppBar/tests/SpeakerMenu.test.tsx b/src/components/AppBar/tests/SpeakerMenu.test.tsx index f1f19ed782..12637fc695 100644 --- a/src/components/AppBar/tests/SpeakerMenu.test.tsx +++ b/src/components/AppBar/tests/SpeakerMenu.test.tsx @@ -15,6 +15,7 @@ import { randomSpeaker } from "types/project"; jest.mock("backend", () => ({ getAllSpeakers: () => mockGetAllSpeakers(), })); +jest.mock("components/Project/ProjectActions", () => ({})); const mockProjId = "mock-project-id"; const mockGetAllSpeakers = jest.fn(); diff --git a/src/components/AppBar/tests/UserMenu.test.tsx b/src/components/AppBar/tests/UserMenu.test.tsx index 40876b8377..4ae1909c9a 100644 --- a/src/components/AppBar/tests/UserMenu.test.tsx +++ b/src/components/AppBar/tests/UserMenu.test.tsx @@ -15,6 +15,7 @@ jest.mock("backend/localStorage", () => ({ getAvatar: jest.fn(), getCurrentUser: jest.fn(), })); +jest.mock("components/Project/ProjectActions", () => ({})); jest.mock("react-router-dom", () => ({ useNavigate: jest.fn(), })); diff --git a/src/components/DataEntry/DataEntryTable/NewEntry/tests/index.test.tsx b/src/components/DataEntry/DataEntryTable/NewEntry/tests/index.test.tsx index 0214822a07..21690692bb 100644 --- a/src/components/DataEntry/DataEntryTable/NewEntry/tests/index.test.tsx +++ b/src/components/DataEntry/DataEntryTable/NewEntry/tests/index.test.tsx @@ -19,6 +19,7 @@ jest.mock( () => (props: any) => mockAutocomplete(props) ); +jest.mock("components/Project/ProjectActions", () => ({})); jest.mock("components/Pronunciations/PronunciationsFrontend", () => "div"); /** Bypass the Autocomplete and render its internal input with the props of both. */ diff --git a/src/components/DataEntry/DataEntryTable/tests/RecentEntry.test.tsx b/src/components/DataEntry/DataEntryTable/tests/RecentEntry.test.tsx index 1101939a44..da04922143 100644 --- a/src/components/DataEntry/DataEntryTable/tests/RecentEntry.test.tsx +++ b/src/components/DataEntry/DataEntryTable/tests/RecentEntry.test.tsx @@ -27,6 +27,8 @@ import { newWritingSystem } from "types/writingSystem"; jest.mock("@mui/material/Autocomplete", () => "div"); +jest.mock("components/Project/ProjectActions", () => ({})); + const mockStore = configureMockStore()(defaultState); const mockVern = "Vernacular"; const mockGloss = "Gloss"; diff --git a/src/components/DataEntry/DataEntryTable/tests/index.test.tsx b/src/components/DataEntry/DataEntryTable/tests/index.test.tsx index baf6075ee7..c6a955cb45 100644 --- a/src/components/DataEntry/DataEntryTable/tests/index.test.tsx +++ b/src/components/DataEntry/DataEntryTable/tests/index.test.tsx @@ -57,6 +57,7 @@ jest.mock( "components/DataEntry/DataEntryTable/RecentEntry", () => MockRecentEntry ); +jest.mock("components/Project/ProjectActions", () => ({})); jest.mock("components/Pronunciations/PronunciationsFrontend", () => "div"); jest.spyOn(window, "alert").mockImplementation(() => {}); diff --git a/src/components/GoalTimeline/tests/GoalRedux.test.tsx b/src/components/GoalTimeline/tests/GoalRedux.test.tsx index 0a62d9bde8..9afc3661c7 100644 --- a/src/components/GoalTimeline/tests/GoalRedux.test.tsx +++ b/src/components/GoalTimeline/tests/GoalRedux.test.tsx @@ -49,6 +49,7 @@ jest.mock("backend", () => ({ jest.mock("browserRouter", () => ({ navigate: (path: Path) => mockNavigate(path), })); +jest.mock("components/Project/ProjectActions", () => ({})); jest.mock("components/Pronunciations/Recorder"); const mockAddGoalToUserEdit = jest.fn(); diff --git a/src/components/GoalTimeline/tests/index.test.tsx b/src/components/GoalTimeline/tests/index.test.tsx index 8ba3017ee0..b2ca67a4cc 100644 --- a/src/components/GoalTimeline/tests/index.test.tsx +++ b/src/components/GoalTimeline/tests/index.test.tsx @@ -19,6 +19,7 @@ jest.mock("components/GoalTimeline/Redux/GoalActions", () => ({ asyncAddGoal: (goal: Goal) => mockChooseGoal(goal), asyncGetUserEdits: () => jest.fn(), })); +jest.mock("components/Project/ProjectActions", () => ({})); jest.mock("components/Pronunciations/Recorder"); jest.mock("types/hooks", () => { return { diff --git a/src/components/Project/tests/ProjectActions.test.tsx b/src/components/Project/tests/ProjectActions.test.tsx index 9660507e1e..602261ce06 100644 --- a/src/components/Project/tests/ProjectActions.test.tsx +++ b/src/components/Project/tests/ProjectActions.test.tsx @@ -8,6 +8,7 @@ import { asyncUpdateCurrentProject, clearCurrentProject, } from "components/Project/ProjectActions"; +import { defaultState as currentProjectState } from "components/Project/ProjectReduxTypes"; import { type RootState, setupStore } from "store"; import { newProject } from "types/project"; import { newUser } from "types/user"; @@ -17,6 +18,8 @@ jest.mock("backend", () => ({ getAllSemanticDomainNames: (lang?: string) => mockGetAllSemDomNames(lang), updateProject: (proj: Project) => mockUpdateProject(proj), })); +// Mock "i18n", else `thrown: "Error: Error: connect ECONNREFUSED ::1:80 [...]` +jest.mock("i18n", () => ({ language: "" })); const mockGetAllProjectUsers = jest.fn(); const mockGetAllSemDomNames = jest.fn(); @@ -40,6 +43,7 @@ describe("ProjectActions", () => { const store = setupStore({ ...persistedDefaultState, currentProjectState: { + ...currentProjectState, project: proj, speaker: {} as Speaker, users: [newUser()], @@ -59,6 +63,7 @@ describe("ProjectActions", () => { const store = setupStore({ ...persistedDefaultState, currentProjectState: { + ...currentProjectState, project: proj, speaker: {} as Speaker, users: [newUser()], @@ -73,21 +78,33 @@ describe("ProjectActions", () => { expect(users).toHaveLength(1); }); - it("fetches semantic domain names when semDomWritingSystem changes", async () => { + it("fetches semantic domain names when empty", async () => { + const store = setupStore({ ...persistedDefaultState }); + + // First project loaded const proj: Project = { ...newProject(), id: mockProjId }; + await store.dispatch(asyncUpdateCurrentProject(proj)); + expect(mockUpdateProject).toHaveBeenCalledTimes(1); + expect(mockGetAllSemDomNames).toHaveBeenCalledTimes(1); + }); + + it("fetches semantic domain names when semDomWritingSystem changes", async () => { const store = setupStore({ ...persistedDefaultState, currentProjectState: { - project: proj, - speaker: {} as Speaker, - users: [newUser()], + ...currentProjectState, + project: { ...newProject(), id: mockProjId }, + semanticDomains: { ["1"]: "one" }, }, }); - await store.dispatch(asyncUpdateCurrentProject({ ...proj, id: "newId" })); + // Project changed but same sem dom language + const proj: Project = { ...newProject(), id: "new-proj-id" }; + await store.dispatch(asyncUpdateCurrentProject(proj)); expect(mockUpdateProject).toHaveBeenCalledTimes(1); expect(mockGetAllSemDomNames).not.toHaveBeenCalled(); + // Sem dom language change in project const lang = "es"; const semDomWritingSystem = { ...proj.semDomWritingSystem, bcp47: lang }; await store.dispatch( @@ -101,41 +118,44 @@ describe("ProjectActions", () => { describe("asyncRefreshProjectUsers", () => { it("correctly affects state", async () => { - const proj: Project = { ...newProject(), id: mockProjId }; const store = setupStore({ ...persistedDefaultState, currentProjectState: { - project: proj, + ...currentProjectState, + project: { ...newProject(), id: mockProjId }, + semanticDomains: { ["1"]: "one" }, speaker: {} as Speaker, - users: [], }, }); const mockUsers = [newUser(), newUser(), newUser()]; mockGetAllProjectUsers.mockResolvedValueOnce(mockUsers); await store.dispatch(asyncRefreshProjectUsers("mockProjId")); - const { project, speaker, users } = store.getState().currentProjectState; - expect(project.id).toEqual(mockProjId); - expect(speaker).not.toBeUndefined(); - expect(users).toHaveLength(mockUsers.length); + const projState = store.getState().currentProjectState; + expect(projState.project.id).toEqual(mockProjId); + expect(projState.semanticDomains).not.toBeUndefined(); + expect(projState.speaker).not.toBeUndefined(); + expect(projState.users).toHaveLength(mockUsers.length); }); }); describe("clearCurrentProject", () => { it("correctly affects state", () => { - const nonDefaultState = { - project: { ...newProject(), id: "nonempty-string" }, - speaker: {} as Speaker, - users: [newUser()], - }; const store = setupStore({ ...persistedDefaultState, - currentProjectState: nonDefaultState, + currentProjectState: { + ...currentProjectState, + project: { ...newProject(), id: mockProjId }, + semanticDomains: { ["1"]: "one" }, + speaker: {} as Speaker, + users: [newUser()], + }, }); store.dispatch(clearCurrentProject()); - const { project, speaker, users } = store.getState().currentProjectState; - expect(project.id).toEqual(""); - expect(speaker).toBeUndefined(); - expect(users).toHaveLength(0); + const projState = store.getState().currentProjectState; + expect(projState.project.id).toEqual(""); + expect(projState.semanticDomains).toBeUndefined(); + expect(projState.speaker).toBeUndefined(); + expect(projState.users).toHaveLength(0); }); }); diff --git a/src/components/ProjectExport/Redux/tests/ExportProjectActions.test.tsx b/src/components/ProjectExport/Redux/tests/ExportProjectActions.test.tsx index 5ff440a77a..55b6d2b109 100644 --- a/src/components/ProjectExport/Redux/tests/ExportProjectActions.test.tsx +++ b/src/components/ProjectExport/Redux/tests/ExportProjectActions.test.tsx @@ -14,6 +14,7 @@ jest.mock("backend", () => ({ downloadLift: (...args: any[]) => mockDownloadList(...args), exportLift: (...args: any[]) => mockExportLift(...args), })); +jest.mock("components/Project/ProjectActions", () => ({})); const mockDownloadList = jest.fn(); const mockExportLift = jest.fn(); diff --git a/src/components/ProjectScreen/tests/CreateProject.test.tsx b/src/components/ProjectScreen/tests/CreateProject.test.tsx index f87d550a1b..0dbffed78c 100644 --- a/src/components/ProjectScreen/tests/CreateProject.test.tsx +++ b/src/components/ProjectScreen/tests/CreateProject.test.tsx @@ -23,6 +23,8 @@ jest.mock("backend", () => ({ projectDuplicateCheck: () => mockProjectDuplicateCheck(), uploadLiftAndGetWritingSystems: () => mockUploadLiftAndGetWritingSystems(), })); +// Mock "i18n", else `thrown: "Error: Error: connect ECONNREFUSED ::1:80 [...]` +jest.mock("i18n", () => ({ language: "" })); const mockProjectDuplicateCheck = jest.fn(); const mockUploadLiftAndGetWritingSystems = jest.fn(); @@ -42,7 +44,6 @@ const mockSubmitEvent = (): Partial> => ({ let projectMaster: ReactTestRenderer; let projectHandle: ReactTestInstance; -4; beforeAll(async () => { await act(async () => { diff --git a/src/components/ProjectSettings/tests/index.test.tsx b/src/components/ProjectSettings/tests/index.test.tsx index c3b9ad80f8..231d3d3b5a 100644 --- a/src/components/ProjectSettings/tests/index.test.tsx +++ b/src/components/ProjectSettings/tests/index.test.tsx @@ -34,6 +34,8 @@ jest.mock("backend", () => ({ getUserRoles: () => Promise.resolve([]), })); jest.mock("components/Project/ProjectActions"); +// Mock "i18n", else `thrown: "Error: Error: connect ECONNREFUSED ::1:80 [...]` +jest.mock("i18n", () => ({ language: "" })); jest.mock("types/hooks", () => { return { ...jest.requireActual("types/hooks"), diff --git a/src/components/SiteSettings/tests/index.test.tsx b/src/components/SiteSettings/tests/index.test.tsx index 42199d14d2..5486df4140 100644 --- a/src/components/SiteSettings/tests/index.test.tsx +++ b/src/components/SiteSettings/tests/index.test.tsx @@ -15,6 +15,7 @@ jest.mock("backend", () => ({ getAllUsers: (...args: any[]) => mockGetAllUsers(...args), getBannerText: (...args: any[]) => mockGetBannerText(...args), })); +jest.mock("components/Project/ProjectActions", () => ({})); const setupMocks = (): void => { mockGetAllProjects.mockResolvedValue([]); diff --git a/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx b/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx index 2ca7dc4820..d1e332c4b0 100644 --- a/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx +++ b/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx @@ -24,6 +24,7 @@ jest.mock("@mui/material", () => { }; }); +jest.mock("components/Project/ProjectActions", () => ({})); jest.mock( "goals/CharacterInventory/CharInv/CharacterDetail/FindAndReplace/FindAndReplaceActions", () => ({ diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/CellComponents/tests/PronunciationsCell.test.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/CellComponents/tests/PronunciationsCell.test.tsx index cf93ffe7d3..5d1b689306 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/CellComponents/tests/PronunciationsCell.test.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/CellComponents/tests/PronunciationsCell.test.tsx @@ -15,6 +15,7 @@ import { StoreState } from "types"; import theme from "types/theme"; import { newPronunciation } from "types/word"; +jest.mock("components/Project/ProjectActions", () => ({})); // Mock the store interactions jest.mock("goals/ReviewEntries/Redux/ReviewEntriesActions", () => ({ deleteAudio: (...args: any[]) => mockDeleteAudio(...args),