Skip to content

Commit

Permalink
[CharacterDetail] Add tests (#2726)
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Oct 26, 2023
1 parent 0209609 commit d36914b
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ interface ReplaceDialogProps {
open: boolean;
dialogFindValue: string;
dialogReplaceValue: string;
handleAccept: () => Promise<void>;
handleCancel: () => void;
handleConfirm: () => Promise<void>;
idCancel?: string;
idConfirm?: string;
}

/**
Expand All @@ -30,7 +32,7 @@ export default function CharacterReplaceDialog(

async function submitFindAndReplace(): Promise<void> {
setLoading(true);
await props.handleAccept();
await props.handleConfirm();
setLoading(false);
}

Expand All @@ -56,12 +58,21 @@ export default function CharacterReplaceDialog(
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={props.handleCancel} variant="outlined" color="primary">
<Button
color="primary"
id={props.idCancel}
onClick={props.handleCancel}
variant="outlined"
>
{t("buttons.cancel")}
</Button>
<LoadingButton
buttonProps={{
color: "primary",
id: props.idConfirm,
onClick: submitFindAndReplace,
}}
loading={loading}
buttonProps={{ onClick: submitFindAndReplace, color: "primary" }}
>
{t("buttons.confirm")}
</LoadingButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import { findAndReplace } from "goals/CharacterInventory/CharInv/CharacterDetail
import { useAppDispatch } from "types/hooks";
import { TextFieldWithFont } from "utilities/fontComponents";

const idPrefix = "find-and-replace";
const fieldIdFind = `${idPrefix}-find-field`;
const fieldIdReplace = `${idPrefix}-replace-field`;
export const buttonIdSubmit = `${idPrefix}-submit-button`;
export const buttonIdCancel = `${idPrefix}-cancel-button`;
export const buttonIdConfirm = `${idPrefix}-confirm-button`;

interface FindAndReplaceProps {
initialFindValue: string;
}
Expand All @@ -25,7 +32,7 @@ export default function FindAndReplace(
useEffect(() => {
setFindValue(props.initialFindValue);
setReplaceValue("");
}, [props.initialFindValue, setFindValue, setReplaceValue]);
}, [props.initialFindValue]);

const dispatchFindAndReplace = async (): Promise<void> => {
await dispatch(findAndReplace(findValue, replaceValue)).catch(() =>
Expand All @@ -45,6 +52,7 @@ export default function FindAndReplace(
{t("charInventory.characterSet.findAndReplace")}
</Typography>
<TextFieldWithFont
id={fieldIdFind}
label={t("charInventory.characterSet.find")}
value={findValue}
onChange={(e) => setFindValue(e.target.value)}
Expand All @@ -55,6 +63,7 @@ export default function FindAndReplace(
vernacular
/>
<TextFieldWithFont
id={fieldIdReplace}
label={t("charInventory.characterSet.replaceWith")}
value={replaceValue}
onChange={(e) => setReplaceValue(e.target.value)}
Expand All @@ -64,15 +73,21 @@ export default function FindAndReplace(
inputProps={{ maxLength: 100 }}
vernacular
/>
<Button color="primary" onClick={() => setWarningDialogOpen(true)}>
<Button
color="primary"
id={buttonIdSubmit}
onClick={() => setWarningDialogOpen(true)}
>
{t("charInventory.characterSet.apply")}
</Button>
<CharacterReplaceDialog
open={warningDialogOpen}
dialogFindValue={findValue}
dialogReplaceValue={replaceValue}
handleCancel={() => setWarningDialogOpen(false)}
handleAccept={dispatchFindAndReplace}
handleConfirm={dispatchFindAndReplace}
idCancel={buttonIdCancel}
idConfirm={buttonIdConfirm}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Provider } from "react-redux";
import {
ReactTestInstance,
ReactTestRenderer,
act,
create,
} from "react-test-renderer";
import configureMockStore from "redux-mock-store";

import "tests/reactI18nextMock";

import CharacterDetail from "goals/CharacterInventory/CharInv/CharacterDetail";
import {
buttonIdCancel,
buttonIdConfirm,
buttonIdSubmit,
} from "goals/CharacterInventory/CharInv/CharacterDetail/FindAndReplace";
import CharacterReplaceDialog from "goals/CharacterInventory/CharInv/CharacterDetail/FindAndReplace/CharacterReplaceDialog";
import { defaultState } from "goals/CharacterInventory/Redux/CharacterInventoryReducer";
import { StoreState } from "types";

// Dialog uses portals, which are not supported in react-test-renderer.
jest.mock("@mui/material", () => {
const materialUiCore = jest.requireActual("@mui/material");
return {
...jest.requireActual("@mui/material"),
Dialog: materialUiCore.Container,
};
});

jest.mock(
"goals/CharacterInventory/CharInv/CharacterDetail/FindAndReplace/FindAndReplaceActions",
() => ({
findAndReplace: () => mockFindAndReplace(),
})
);
jest.mock("types/hooks", () => {
return {
...jest.requireActual("types/hooks"),
useAppDispatch: () => (args: any) => Promise.resolve(args),
};
});

const mockClose = jest.fn();
const mockFindAndReplace = jest.fn();

let charMaster: ReactTestRenderer;

const mockChar = "#";
// mockPrefix is a single character whose only appearance in the component
// is in an example of a word containing the mockChar.
const mockPrefix = "@";
const mockWord = mockPrefix + mockChar;
const mockState: Partial<StoreState> = {
characterInventoryState: { ...defaultState, allWords: [mockWord] },
};
const mockStore = configureMockStore()(mockState);

async function renderCharacterDetail(): Promise<void> {
await act(async () => {
charMaster = create(
<Provider store={mockStore}>
<CharacterDetail character={mockChar} close={mockClose} />
</Provider>
);
});
}

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();
});

describe("CharacterDetail", () => {
it("renders with example word", () => {
expect(hasText(charMaster.root, mockPrefix)).toBeTruthy();
});

describe("FindAndReplace", () => {
it("has working dialog", async () => {
const dialog = charMaster.root.findByType(CharacterReplaceDialog);
const submitButton = charMaster.root.findByProps({ id: buttonIdSubmit });
const cancelButton = charMaster.root.findByProps({ id: buttonIdCancel });
const confButton = charMaster.root.findByProps({ id: buttonIdConfirm });

expect(dialog.props.open).toBeFalsy();
await act(async () => {
submitButton.props.onClick();
});
expect(dialog.props.open).toBeTruthy();
await act(async () => {
cancelButton.props.onClick();
});
expect(dialog.props.open).toBeFalsy();
await act(async () => {
submitButton.props.onClick();
});
expect(dialog.props.open).toBeTruthy();
await act(async () => {
await confButton.props.onClick();
});
expect(dialog.props.open).toBeFalsy();
});

it("only submits after confirmation", async () => {
const submitButton = charMaster.root.findByProps({ id: buttonIdSubmit });
const cancelButton = charMaster.root.findByProps({ id: buttonIdCancel });
const confButton = charMaster.root.findByProps({ id: buttonIdConfirm });

await act(async () => {
submitButton.props.onClick();
cancelButton.props.onClick();
submitButton.props.onClick();
});
expect(mockFindAndReplace).not.toBeCalled();
await act(async () => {
await confButton.props.onClick();
});
expect(mockFindAndReplace).toBeCalled();
});
});
});

0 comments on commit d36914b

Please sign in to comment.