Skip to content

Commit

Permalink
Basic dialog tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bduhbya committed May 19, 2024
1 parent e6f9ded commit 9ea1211
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 52 deletions.
3 changes: 1 addition & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"version": "0.2.0",
"configurations": [

{
"type": "msedge",
"request": "launch",
Expand All @@ -10,4 +9,4 @@
"webRoot": "${workspaceFolder}"
}
]
}
}
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"ts-node": "^10.9.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.0",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^14.2.0",
"@types/jest": "^29.5.11",
"@types/node": "^20",
Expand Down
61 changes: 61 additions & 0 deletions src/app/components/BasicDialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import {
DialogType,
DialogData,
BasicDialog,
dialogDetails,
} from "./BasicDialog";

const getExpectedTitle = (type: DialogType) => {
const { title, icon } = dialogDetails[type];
return icon + " " + title;
};

const message = "Test message";

describe("BasicDialog", () => {
it("renders info message", () => {
const type = DialogType.INFO;
const dialogData = new DialogData(message, type);
const { getByText } = render(
<BasicDialog dialogData={dialogData} onConfirm={() => {}} />,
);
const dialogTitle = getExpectedTitle(type);
expect(getByText(dialogTitle)).toBeInTheDocument();
expect(getByText(message)).toBeInTheDocument();
});

it("renders warning message", () => {
const type = DialogType.WARNING;
const dialogData = new DialogData(message, type);
const { getByText } = render(
<BasicDialog dialogData={dialogData} onConfirm={() => {}} />,
);
const dialogTitle = getExpectedTitle(type);
expect(getByText(dialogTitle)).toBeInTheDocument();
expect(getByText(message)).toBeInTheDocument();
});

it("renders error message", () => {
const type = DialogType.ERROR;
const dialogData = new DialogData(message, type);
const { getByText } = render(
<BasicDialog dialogData={dialogData} onConfirm={() => {}} />,
);
const dialogTitle = getExpectedTitle(type);
expect(getByText(dialogTitle)).toBeInTheDocument();
expect(getByText(message)).toBeInTheDocument();
});

it("calls onConfirm when confirm button is clicked", () => {
const dialogData = new DialogData(message, DialogType.INFO);
const onConfirm = jest.fn();
const { getByText } = render(
<BasicDialog dialogData={dialogData} onConfirm={onConfirm} />,
);
fireEvent.click(getByText("Confirm"));
expect(onConfirm).toHaveBeenCalled();
});
});
33 changes: 21 additions & 12 deletions src/app/components/BasicDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
import React from 'react';
import React from "react";
import strings from "@/strings";

export enum DialogType {
WARNING = 'Warning',
ERROR = 'Error',
INFO = 'Info'
WARNING = "Warning",
ERROR = "Error",
INFO = "Info",
}

interface DialogDetail {
title: string;
icon: string;
}

const dialogDetails: Record<DialogType, DialogDetail> = {
[DialogType.WARNING]: { title: 'Warning', icon: '⚠️' },
[DialogType.ERROR]: { title: 'Error', icon: '❌' },
[DialogType.INFO]: { title: 'Info', icon: 'ℹ️' },
export const dialogDetails: Record<DialogType, DialogDetail> = {
[DialogType.WARNING]: {
title: strings.warningTitle,
icon: strings.warningIcon,
},
[DialogType.ERROR]: { title: strings.errorTitle, icon: strings.errorIcon },
[DialogType.INFO]: { title: strings.infoTitle, icon: strings.infoIcon },
};

export class DialogData {
constructor(public message: string, public dialogType: DialogType) {}
constructor(
public message: string,
public dialogType: DialogType,
) {}
}

export interface BasicDialogProps {
dialogData: DialogData;
onConfirm: () => void;
}

export const BasicDialog: React.FC<BasicDialogProps> = ({ dialogData, onConfirm }) => {

export const BasicDialog: React.FC<BasicDialogProps> = ({
dialogData,
onConfirm,
}) => {
const handleConfirm = () => {
onConfirm();
};
Expand All @@ -53,4 +62,4 @@ export const BasicDialog: React.FC<BasicDialogProps> = ({ dialogData, onConfirm
);
};

export default BasicDialog;
export default BasicDialog;
58 changes: 37 additions & 21 deletions src/app/components/CombatTracker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@ import "@testing-library/jest-dom";
import React from "react";
import strings from "../../strings";
import { mockCharacterFile } from "../lib/definitionMocks";
import { promptForFile } from '../lib/fileInput';
import { InitiativeInputDialogProps } from './InitiativeInputDialog';
import { Character } from '../lib/definitions';
import { promptForFile } from "../lib/fileInput";
import { InitiativeInputDialogProps } from "./InitiativeInputDialog";
import { Character } from "../lib/definitions";

var handleCancelHook: () => void;
var handleConfirmHook: (newCharacter: Character) => void;
var addedCharacter: Character;

jest.mock('../lib/fileInput', () => ({
jest.mock("../lib/fileInput", () => ({
promptForFile: jest.fn(),
}));

jest.mock('./InitiativeInputDialog', () => (props: InitiativeInputDialogProps) => {
handleCancelHook = props.onCancel;
handleConfirmHook = props.onConfirm;
addedCharacter = props.character;
return <div>Mock InitiativeInputDialog</div>;
});
jest.mock(
"./InitiativeInputDialog",
() => (props: InitiativeInputDialogProps) => {
handleCancelHook = props.onCancel;
handleConfirmHook = props.onConfirm;
addedCharacter = props.character;
return <div>Mock InitiativeInputDialog</div>;
},
);

describe("CombatTracker", () => {
it("renders correctly", () => {
it("renders component initial ui", () => {
const { getByText } = render(<CombatTracker />);
// const handleAddToCombat = jest.fn();
// const handleToggleSortDescending = jest.fn();
Expand Down Expand Up @@ -68,50 +71,63 @@ describe("CombatTracker", () => {

it("handles null file input", async () => {
const { getByText } = render(<CombatTracker />);
(promptForFile as jest.MockedFunction<typeof promptForFile>).mockResolvedValue(null);
(
promptForFile as jest.MockedFunction<typeof promptForFile>
).mockResolvedValue(null);

fireEvent.click(getByText(strings.addToCombatButton));

// Wait for promises to resolve
await new Promise((resolve => setTimeout(resolve, 0)));
await new Promise((resolve) => setTimeout(resolve, 0));

await waitFor(() =>
expect(getByText("Mock InitiativeInputDialog")).toBeInTheDocument(),
);
// Check that the error handling code was executed
// This depends on how your component handles the error.
// For example, if it shows an error message, you can check that the message is displayed:
expect(getByText('No file selected')).toBeInTheDocument();
expect(getByText(strings.fileNotSelected)).toBeInTheDocument();
// TODO: Mock the file input and FileReader to test adding a character
// TODO: may need to create a file loading component to test this
});

it("handles file input error", async () => {
const { getByText } = render(<CombatTracker />);
(promptForFile as jest.MockedFunction<typeof promptForFile>).mockRejectedValue(new Error('File input error'));
(
promptForFile as jest.MockedFunction<typeof promptForFile>
).mockRejectedValue(new Error("File input error"));

fireEvent.click(getByText(strings.addToCombatButton));

// Wait for promises to resolve
await new Promise((resolve => setTimeout(resolve, 0)));
await new Promise((resolve) => setTimeout(resolve, 0));

// Check that the error handling code was executed
// This depends on how your component handles the error.
// For example, if it shows an error message, you can check that the message is displayed:
expect(getByText('No file selected')).toBeInTheDocument();
expect(getByText("No file selected")).toBeInTheDocument();
// TODO: Mock the file input and FileReader to test adding a character
// TODO: may need to create a file loading component to test this
});

it("inserts character", async () => {
const { getByText } = render(<CombatTracker />);
(promptForFile as jest.MockedFunction<typeof promptForFile>).mockResolvedValue(mockCharacterFile);
(
promptForFile as jest.MockedFunction<typeof promptForFile>
).mockResolvedValue(mockCharacterFile);

fireEvent.click(getByText(strings.addToCombatButton));

// Wait for promises to resolve
await new Promise((resolve => setTimeout(resolve, 0)));
await new Promise((resolve) => setTimeout(resolve, 0));

await waitFor(() => expect(getByText("Mock InitiativeInputDialog")).toBeInTheDocument());
await waitFor(() =>
expect(getByText("Mock InitiativeInputDialog")).toBeInTheDocument(),
);
handleConfirmHook(addedCharacter);
await waitFor(() => expect(getByText(addedCharacter.name)).toBeInTheDocument());
await waitFor(() =>
expect(getByText(addedCharacter.name)).toBeInTheDocument(),
);
expect(getByText(addedCharacter.initiative.toString())).toBeInTheDocument();
});

Expand Down
17 changes: 10 additions & 7 deletions src/app/components/CombatTracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import InitiativeInputDialog from "./InitiativeInputDialog";
import { CheckmarkIconPositive } from "../lib/SVGIcons";
import strings from "@/strings";
import { promptForFile } from "../lib/fileInput";
import {BasicDialog, DialogData, DialogType} from "./BasicDialog";
import { BasicDialog, DialogData, DialogType } from "./BasicDialog";

const CombatTracker: React.FC = () => {
const DIRECTION_UP = "up";
const DIRECTION_DOWN = "down";
type Direction = typeof DIRECTION_UP | typeof DIRECTION_DOWN;
const FILE_NOT_SELECTED = "No file selected";
const FILE_PARSING_ERROR = "Unable to parse JSON file";

const [combatCharacters, setCombatCharacters] = useState<Character[]>([]);
const [sortDescending, toggleSortDescending] = useState(true);
Expand All @@ -23,7 +21,9 @@ const CombatTracker: React.FC = () => {
const [pendingCharacter, setPendingCharacter] = useState<Character | null>(
null,
);
const [currentDialogData, setCurrentDialogData] = useState<DialogData | null>(null);
const [currentDialogData, setCurrentDialogData] = useState<DialogData | null>(
null,
);

const handleToggleSortDescending = () => {
// Toggle the sort order
Expand Down Expand Up @@ -55,14 +55,18 @@ const CombatTracker: React.FC = () => {
initiative: 0,
});
} catch (error) {
setCurrentDialogData(new DialogData(FILE_PARSING_ERROR, DialogType.WARNING));
setCurrentDialogData(
new DialogData(strings.fileParsingError, DialogType.WARNING),
);
console.error("Error parsing JSON file:", error);
}
};

reader.readAsText(file);
} else {
setCurrentDialogData(new DialogData(FILE_NOT_SELECTED, DialogType.WARNING));
setCurrentDialogData(
new DialogData(strings.fileNotSelected, DialogType.WARNING),
);
console.log("No file selected");
}
};
Expand Down Expand Up @@ -149,7 +153,6 @@ const CombatTracker: React.FC = () => {
dialogData={currentDialogData}
onConfirm={() => setCurrentDialogData(null)}
/>

)}
{/* Render the custom initiative input dialog */}
{pendingCharacter && (
Expand Down
13 changes: 9 additions & 4 deletions src/app/lib/definitionMocks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Character } from "./definitions";
import path from 'path';
import fs from 'fs';
import path from "path";
import fs from "fs";

const mockCharacterDataPath = path.join(__dirname, '..', 'testData', 'character_data.json');
const mockCharacterData = fs.readFileSync(mockCharacterDataPath, 'utf8');;
const mockCharacterDataPath = path.join(
__dirname,
"..",
"testData",
"character_data.json",
);
const mockCharacterData = fs.readFileSync(mockCharacterDataPath, "utf8");

const mockCharacterDataParsed = JSON.parse(mockCharacterData);
const characterFile = new File([mockCharacterData], mockCharacterDataPath);
Expand Down
13 changes: 13 additions & 0 deletions src/strings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// src/strings.ts

import { warn } from "console";

export default {
// Common strings
cancelString: "Cancel",
Expand All @@ -11,6 +13,8 @@ export default {
initiativeColumnLabel: "Initiative",
ascendingLabel: "Ascending",
descendingLabel: "Descending",
fileNotSelected:"No file selected",
fileParsingError: "Unable to parse JSON file",

// InitiativeInputDialog strings
initiativePrompt: "Enter Initiative",
Expand All @@ -19,4 +23,13 @@ export default {
initiativeLabel: "Initiative:",
characterLabel: "Character Name:",
addCharacterButton: "Add Character",

// BasicDialog strings
warningTitle: "Warning",
errorTitle: "Error",
infoTitle: "Info",
confirmButton: "Confirm",
warningIcon: "⚠️",
errorIcon: "❌",
infoIcon: "ℹ️",
};

0 comments on commit 9ea1211

Please sign in to comment.