From 71b75cc92ae2374823db3d777fe821fc768e3947 Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Wed, 14 Feb 2024 09:44:56 -0500 Subject: [PATCH] Extract test function as general-use utility (#2947) --- .../GoalTimeline/tests/GoalRedux.test.tsx | 2 +- .../tests/ChooseProject.test.tsx | 22 +++++++------------ .../CharacterDetail/tests/index.test.tsx | 19 ++++------------ src/utilities/testRendererUtilities.tsx | 13 +++++++++++ ...lities.tsx => testingLibraryUtilities.tsx} | 12 +++++----- 5 files changed, 31 insertions(+), 37 deletions(-) create mode 100644 src/utilities/testRendererUtilities.tsx rename src/utilities/{testUtilities.tsx => testingLibraryUtilities.tsx} (77%) diff --git a/src/components/GoalTimeline/tests/GoalRedux.test.tsx b/src/components/GoalTimeline/tests/GoalRedux.test.tsx index 906e4b1438..0a62d9bde8 100644 --- a/src/components/GoalTimeline/tests/GoalRedux.test.tsx +++ b/src/components/GoalTimeline/tests/GoalRedux.test.tsx @@ -33,7 +33,7 @@ import { GoalStatus, GoalType } from "types/goals"; import { Path } from "types/path"; import { newUser } from "types/user"; import * as goalUtilities from "utilities/goalUtilities"; -import { renderWithProviders } from "utilities/testUtilities"; +import { renderWithProviders } from "utilities/testingLibraryUtilities"; jest.mock("backend", () => ({ addGoalToUserEdit: (...args: any[]) => mockAddGoalToUserEdit(...args), diff --git a/src/components/ProjectScreen/tests/ChooseProject.test.tsx b/src/components/ProjectScreen/tests/ChooseProject.test.tsx index c65824877a..1c9a838d7f 100644 --- a/src/components/ProjectScreen/tests/ChooseProject.test.tsx +++ b/src/components/ProjectScreen/tests/ChooseProject.test.tsx @@ -3,9 +3,10 @@ import renderer from "react-test-renderer"; import "tests/reactI18nextMock"; -import { Project } from "api/models"; +import { type Project } from "api/models"; import ChooseProject from "components/ProjectScreen/ChooseProject"; import { newProject } from "types/project"; +import { testInstanceHasText } from "utilities/testRendererUtilities"; import { randomIntString } from "utilities/utilities"; jest.mock("backend", () => ({ @@ -27,13 +28,6 @@ const mockProj = (name: string): Project => ({ let testRenderer: renderer.ReactTestRenderer; -const hasText = (item: renderer.ReactTestInstance, text: string): boolean => { - const found = item.findAll( - (node) => node.children.length === 1 && node.children[0] === text - ); - return found.length !== 0; -}; - it("renders with projects in alphabetical order", async () => { const unordered = ["In the middle", "should be last", "alphabetically first"]; mockGetProjects.mockResolvedValue(unordered.map((name) => mockProj(name))); @@ -42,10 +36,10 @@ it("renders with projects in alphabetical order", async () => { }); const items = testRenderer.root.findAllByType(ListItemButton); expect(items).toHaveLength(unordered.length); - expect(hasText(items[0], unordered[0])).toBeFalsy; - expect(hasText(items[1], unordered[1])).toBeFalsy; - expect(hasText(items[2], unordered[2])).toBeFalsy; - expect(hasText(items[0], unordered[2])).toBeTruthy; - expect(hasText(items[1], unordered[0])).toBeTruthy; - expect(hasText(items[2], unordered[1])).toBeTruthy; + expect(testInstanceHasText(items[0], unordered[0])).toBeFalsy(); + expect(testInstanceHasText(items[1], unordered[1])).toBeFalsy(); + expect(testInstanceHasText(items[2], unordered[2])).toBeFalsy(); + expect(testInstanceHasText(items[0], unordered[2])).toBeTruthy(); + expect(testInstanceHasText(items[1], unordered[0])).toBeTruthy(); + expect(testInstanceHasText(items[2], unordered[1])).toBeTruthy(); }); diff --git a/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx b/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx index 0404a736a4..2ca7dc4820 100644 --- a/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx +++ b/src/goals/CharacterInventory/CharInv/CharacterDetail/tests/index.test.tsx @@ -1,10 +1,5 @@ import { Provider } from "react-redux"; -import { - ReactTestInstance, - ReactTestRenderer, - act, - create, -} from "react-test-renderer"; +import { type ReactTestRenderer, act, create } from "react-test-renderer"; import configureMockStore from "redux-mock-store"; import "tests/reactI18nextMock"; @@ -17,7 +12,8 @@ import { } from "goals/CharacterInventory/CharInv/CharacterDetail/FindAndReplace"; import CharacterReplaceDialog from "goals/CharacterInventory/CharInv/CharacterDetail/FindAndReplace/CharacterReplaceDialog"; import { defaultState } from "goals/CharacterInventory/Redux/CharacterInventoryReduxTypes"; -import { StoreState } from "types"; +import { type StoreState } from "types"; +import { testInstanceHasText } from "utilities/testRendererUtilities"; // Dialog uses portals, which are not supported in react-test-renderer. jest.mock("@mui/material", () => { @@ -66,13 +62,6 @@ async function renderCharacterDetail(): Promise { }); } -const hasText = (item: ReactTestInstance, text: string): boolean => { - const found = item.findAll( - (node) => node.children.length === 1 && node.children[0] === text - ); - return found.length !== 0; -}; - beforeEach(async () => { jest.resetAllMocks(); await renderCharacterDetail(); @@ -80,7 +69,7 @@ beforeEach(async () => { describe("CharacterDetail", () => { it("renders with example word", () => { - expect(hasText(charMaster.root, mockPrefix)).toBeTruthy(); + expect(testInstanceHasText(charMaster.root, mockPrefix)).toBeTruthy(); }); describe("FindAndReplace", () => { diff --git a/src/utilities/testRendererUtilities.tsx b/src/utilities/testRendererUtilities.tsx new file mode 100644 index 0000000000..7bdf639ca7 --- /dev/null +++ b/src/utilities/testRendererUtilities.tsx @@ -0,0 +1,13 @@ +import { type ReactTestInstance } from "react-test-renderer"; + +/** Checks if any node in the given `react-test-renderer` instance has the given text. */ +export function testInstanceHasText( + instance: ReactTestInstance, + text: string +): boolean { + return ( + instance.findAll( + (node) => node.children.length === 1 && node.children[0] === text + ).length > 0 + ); +} diff --git a/src/utilities/testUtilities.tsx b/src/utilities/testingLibraryUtilities.tsx similarity index 77% rename from src/utilities/testUtilities.tsx rename to src/utilities/testingLibraryUtilities.tsx index 0f7020dcec..3eb1d7f7aa 100644 --- a/src/utilities/testUtilities.tsx +++ b/src/utilities/testingLibraryUtilities.tsx @@ -7,18 +7,16 @@ import { PersistGate } from "redux-persist/integration/react"; import { defaultState } from "components/App/DefaultState"; import { type AppStore, type RootState, persistor, setupStore } from "store"; -// These test utilities are leveraged from the Redux documentation for Writing Tests: -// https://redux.js.org/usage/writing-tests -// Specifically, see the section on "Integration Testing Connected Components -// and Redux Logic" - -// This type interface extends the default options for render from RTL, as well -// as allows the user to specify other things such as initialState, store. +/** This extends the default options for `render` from `@testing-library/react`, + * allowing the user to specify other things such as `initialState`, `store`. */ interface ExtendedRenderOptions extends Omit { preloadedState?: PreloadedState; store?: AppStore; } +/** This test utility is leveraged from the Redux documentation for Writing Tests: + * https://redux.js.org/usage/writing-tests. Specifically, see the section on + * "Integration Testing Connected Components and Redux Logic" */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function renderWithProviders( ui: ReactElement,