This commit is contained in:
Michael Zhang 2023-06-15 20:18:38 -05:00
parent 0edff29b8c
commit 51d635c452
14 changed files with 3168 additions and 811 deletions

2
.gitignore vendored
View file

@ -26,3 +26,5 @@ dist-ssr
houhou.db houhou.db
src/data/kanadata.json src/data/kanadata.json
.direnv .direnv
/coverage

10
jest.config.js Normal file
View file

@ -0,0 +1,10 @@
export default {
preset: "vite-jest",
setupFilesAfterEnv: ["<rootDir>/src/setupTests.js"],
testMatch: [
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}",
],
testEnvironment: "jest-environment-jsdom",
};

3889
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"preview": "vite preview", "preview": "vite preview",
"test": "vitest",
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
@ -29,23 +30,27 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^8.42.0", "@eslint/js": "^8.42.0",
"@swc/core": "^1.3.64",
"@tauri-apps/cli": "^1.4.0", "@tauri-apps/cli": "^1.4.0",
"@testing-library/react": "^14.0.0",
"@types/lodash-es": "^4.17.7", "@types/lodash-es": "^4.17.7",
"@types/node": "^18.7.10", "@types/node": "^20.3.1",
"@types/react": "^18.0.15", "@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/react-timeago": "^4.1.3", "@types/react-timeago": "^4.1.3",
"@typescript-eslint/eslint-plugin": "^5.59.11", "@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11", "@typescript-eslint/parser": "^5.59.11",
"@vitejs/plugin-react": "^3.0.0", "@vitejs/plugin-react": "^4.0.0",
"@vitejs/plugin-react-swc": "^3.3.2", "@vitejs/plugin-react-swc": "^3.3.2",
"@vitest/coverage-v8": "^0.32.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"eslint": "^8.42.0", "eslint": "^8.42.0",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.32.2",
"postcss": "^8.4.24", "postcss": "^8.4.24",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"sass": "^1.62.1", "sass": "^1.62.1",
"typescript": "^4.9.5", "typescript": "^5.1.3",
"vite": "^4.2.1" "vite": "^4.2.1",
"vitest": "^0.32.0"
} }
} }

View file

@ -5,11 +5,11 @@ import { createBrowserRouter } from "react-router-dom";
import { Outlet, Route, createRoutesFromElements, matchPath, useLocation } from "react-router"; import { Outlet, Route, createRoutesFromElements, matchPath, useLocation } from "react-router";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { Component as KanjiPane } from "./panes/KanjiPane"; import KanjiPane from "./panes/KanjiPane";
import { Component as SettingsPane } from "./panes/SettingsPane"; import SettingsPane from "./panes/SettingsPane";
import { Component as SrsPane } from "./panes/SrsPane"; import SrsPane from "./panes/SrsPane";
import { Component as SrsReviewPane } from "./panes/SrsReviewPane"; import SrsReviewPane from "./panes/SrsReviewPane";
import { Component as VocabPane } from "./panes/VocabPane"; import VocabPane from "./panes/VocabPane";
import styles from "./App.module.scss"; import styles from "./App.module.scss";

View file

@ -21,6 +21,7 @@ export default function DashboardItemStats({ srsStats }: DashboardItemStatsProps
{srsLevels.groups.map((group) => { {srsLevels.groups.map((group) => {
const groupLevels = srsLevelsByGroups.get(group); const groupLevels = srsLevelsByGroups.get(group);
if (!groupLevels) return null; if (!groupLevels) return null;
groupLevels.sort((a, b) => (a.delay == null || b.delay == null ? 0 : a.delay - b.delay));
const groupCount = groupLevels const groupCount = groupLevels
.map((level) => grades.get(level.value) ?? 0) .map((level) => grades.get(level.value) ?? 0)

View file

@ -16,7 +16,7 @@ export interface GetKanjiResult {
kanji: Kanji[]; kanji: Kanji[];
} }
export function Component() { export default function KanjiPane() {
const { selectedKanji } = useParams(); const { selectedKanji } = useParams();
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
@ -85,5 +85,3 @@ export function Component() {
</Flex> </Flex>
); );
} }
Component.displayName = "KanjiPane";

View file

@ -11,7 +11,7 @@ interface ApplicationInfo {
srs_db_path: string; srs_db_path: string;
} }
export function Component() { export default function SettingsPane() {
const { data: info } = useSWR("application_info", () => const { data: info } = useSWR("application_info", () =>
invoke<ApplicationInfo>("application_info"), invoke<ApplicationInfo>("application_info"),
); );
@ -55,5 +55,3 @@ export function Component() {
</main> </main>
); );
} }
Component.displayName = "SettingsPane";

View file

@ -19,7 +19,7 @@ export interface SrsStats {
num_failure: number; num_failure: number;
} }
export function Component() { export default function SrsPane() {
const { data: srsStats, error } = useSWR(["get_srs_stats"], ([command]) => const { data: srsStats, error } = useSWR(["get_srs_stats"], ([command]) =>
invoke<SrsStats>(command), invoke<SrsStats>(command),
); );
@ -38,5 +38,3 @@ export function Component() {
</main> </main>
); );
} }
Component.displayName = "SrsPane";

View file

@ -0,0 +1,15 @@
import SrsReviewPane from "./SrsReviewPane";
import { wrappedRender } from "../test/setup";
import { vi, Mock } from "vitest";
describe("SrsReviewPane", () => {
beforeEach(() => {});
afterEach(() => {
vi.restoreAllMocks();
});
test("renders without exploding", () => {
wrappedRender(<SrsReviewPane />);
});
});

View file

@ -18,7 +18,7 @@ import {
import InputBox from "../components/srsReview/InputBox"; import InputBox from "../components/srsReview/InputBox";
import SelectOnClick from "../components/utils/SelectOnClick"; import SelectOnClick from "../components/utils/SelectOnClick";
export function Component() { export default function SrsReviewPane() {
// null = has not started, (.length == 0) = finished // null = has not started, (.length == 0) = finished
const [reviewQueue, setReviewQueue] = useState<ReviewItem[] | null>(null); const [reviewQueue, setReviewQueue] = useState<ReviewItem[] | null>(null);
const [completedQueue, setCompletedQueue] = useState<ReviewItem[]>([]); const [completedQueue, setCompletedQueue] = useState<ReviewItem[]>([]);
@ -147,7 +147,9 @@ export function Component() {
/> />
)} )}
<h1 className={styles.testWord}>{nextItem.challenge}</h1> <h1 className={styles.testWord} data-testid="testWord">
{nextItem.challenge}
</h1>
<InputBox <InputBox
submit={formSubmit} submit={formSubmit}
@ -196,5 +198,3 @@ export function Component() {
</main> </main>
); );
} }
Component.displayName = "SrsReviewPane";

View file

@ -1,12 +1,10 @@
import VocabList from "../components/VocabList"; import VocabList from "../components/VocabList";
import styles from "./VocabPane.module.scss"; import styles from "./VocabPane.module.scss";
export function Component() { export default function VocabPane() {
return ( return (
<main className={styles.main}> <main className={styles.main}>
<VocabList /> <VocabList />
</main> </main>
); );
} }
Component.displayName = "VocabPane";

7
src/test/setup.ts Normal file
View file

@ -0,0 +1,7 @@
import { render } from "@testing-library/react";
import { ReactElement } from "react";
import { BrowserRouter } from "react-router-dom";
export function wrappedRender(ui: ReactElement) {
return render(ui, { wrapper: BrowserRouter });
}

8
vitest.config.ts Normal file
View file

@ -0,0 +1,8 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "jsdom",
},
});