diff --git a/tests/commonUtils.ts b/tests/commonUtils.ts new file mode 100644 index 0000000..6a2cbe6 --- /dev/null +++ b/tests/commonUtils.ts @@ -0,0 +1,13 @@ +import { MockResponseInit } from "jest-fetch-mock"; + +export const htmlRes = (translation: string, className = "result-container") => ` +
+
+ ${translation} +
+
+`; + +export const resolveFetchWith = (params: string | MockResponseInit) => ( + fetchMock.mockResponseOnce(async () => params) +); diff --git a/tests/pages/[[...slug]].test.tsx b/tests/pages/[[...slug]].test.tsx index be26efb..fff761d 100644 --- a/tests/pages/[[...slug]].test.tsx +++ b/tests/pages/[[...slug]].test.tsx @@ -1,18 +1,12 @@ import { screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import { htmlRes, resolveFetchWith } from "../commonUtils"; import Router from "next/router"; import { getPage } from "next-page-tester"; import faker from "faker"; import { getStaticProps } from "../../pages/[[...slug]]"; -const mockPush = jest.fn().mockImplementation(async () => true); -Router.push = mockPush; - -const html = (translation: string) => ` -
- ${translation} -
-`; +const mockPush = jest.spyOn(Router, "push").mockImplementation(async () => true); beforeEach(() => { fetchMock.resetMocks(); @@ -45,7 +39,8 @@ describe("getStaticProps", () => { it("returns translation & initial values on 3 params", async () => { const translation = faker.random.words(); - fetchMock.mockResponseOnce(async () => ({ body: html(translation) })); + const html = htmlRes(translation); + resolveFetchWith(html); const slug = [source, target, query]; expect(await getStaticProps({ params: { slug } })).toStrictEqual({ @@ -72,6 +67,10 @@ describe("Page", () => { const query = screen.getByRole("textbox", { name: /query/i }); userEvent.type(query, faker.random.words()); + await waitFor( + () => expect(Router.push).not.toHaveBeenCalled(), + { timeout: 250 } + ); await waitFor( () => expect(Router.push).toHaveBeenCalledTimes(1), { timeout: 2500 } @@ -82,10 +81,10 @@ describe("Page", () => { const initial = { source: "ca", target: "es", - query: faker.random.words() + query: encodeURIComponent(faker.random.words()) }; const translation = faker.random.words(); - fetchMock.mockResponseOnce(async () => ({ body: html(translation) })); + resolveFetchWith(htmlRes(translation)); const { render } = await getPage({ route: `/${initial.source}/${initial.target}/${initial.query}` @@ -93,22 +92,22 @@ describe("Page", () => { render(); expect(await screen.findByText(translation)).toBeVisible(); - const query = screen.getByRole("textbox", { name: /query/i }); - expect(query).toHaveValue(encodeURIComponent(initial.query)); const source = screen.getByRole("combobox", { name: /source/i }); expect(source).toHaveValue(initial.source); const target = screen.getByRole("combobox", { name: /target/i }); expect(target).toHaveValue(initial.target); + const query = screen.getByRole("textbox", { name: /query/i }); + expect(query).toHaveValue(initial.query); }); it("parses urlencoding correctly", async () => { const initial = { source: "zh", target: "en", - query: "你好" + query: encodeURIComponent("你好") }; const translation = "Hello"; - fetchMock.mockResponseOnce(async () => ({ body: html(translation) })); + resolveFetchWith(htmlRes(translation)); const { render } = await getPage({ route: `/${initial.source}/${initial.target}/${initial.query}` @@ -121,11 +120,11 @@ describe("Page", () => { const target = screen.getByRole("combobox", { name: /target/i }); expect(target).toHaveValue(initial.target); const query = screen.getByRole("textbox", { name: /query/i }); - expect(query).toHaveValue(encodeURIComponent(initial.query)); + expect(query).toHaveValue(initial.query); }); it("switches the page on language change", async () => { - fetchMock.mockResponseOnce(async () => ({ body: html(faker.random.words()) })); + resolveFetchWith(htmlRes(faker.random.words())); const { render } = await getPage({ route: `/auto/en/${faker.random.words()}` diff --git a/tests/utils/language.test.ts b/tests/utils/language.test.ts index 3c3d958..e42bf9d 100644 --- a/tests/utils/language.test.ts +++ b/tests/utils/language.test.ts @@ -1,46 +1,51 @@ import faker from "faker"; -import { replaceBoth, retrieveFiltered } from "../../utils/language"; +import { replaceBoth, retrieveFiltered, CheckType, LangType } from "../../utils/language"; import { languages, exceptions, mappings } from "../../utils/languages.json"; describe("replaceBoth", () => { + const testReplacer = ( + checkType: CheckType, + checkObj: { + [key in LangType]: { + [key: string]: string + } + }, + langType: LangType + ) => ( + Object.entries(checkObj[langType]).forEach(([code, replacement]) => { + const res = replaceBoth(checkType, { source: "", target: "", [langType]: code }) + expect(res[langType]).toBe(replacement); + }) + ); + it("replaces excepted sources correctly", () => { - Object.entries(exceptions.source).forEach(([code, replacement]) => { - const { source } = replaceBoth("exception", { source: code, target: "" }) - expect(source).toBe(replacement); - }); + testReplacer("exception", exceptions, "source"); }); it("replaces excepted targets correctly", () => { - Object.entries(exceptions.target).forEach(([code, replacement]) => { - const { target } = replaceBoth("exception", { source: "", target: code }) - expect(target).toBe(replacement); - }); + testReplacer("exception", exceptions, "target"); }); it("replaces mapped sources correctly", () => { - Object.entries(mappings.source).forEach(([code, replacement]) => { - const { source } = replaceBoth("mapping", { source: code, target: "" }) - expect(source).toBe(replacement); - }); + testReplacer("mapping", mappings, "source"); }); it("replaces mapped targets correctly", () => { - Object.entries(mappings.target).forEach(([code, replacement]) => { - const { target } = replaceBoth("mapping", { source: "", target: code }) - expect(target).toBe(replacement); - }); + testReplacer("mapping", mappings, "target"); }); }); describe("retrieveFiltered", () => { + const filteredEntries = (langType: LangType, current: string) => ( + Object.entries(languages).filter(([code]) => !Object.keys(exceptions[langType]).includes(code) && code !== current) + ); + it("filters by exceptions & by opposite values", () => { const source = faker.random.locale(); const target = faker.random.locale(); - const sourceKeys = Object.keys(languages).filter(code => !Object.keys(exceptions.source).includes(code) && code !== target); - const targetKeys = Object.keys(languages).filter(code => !Object.keys(exceptions.target).includes(code) && code !== source); const { sourceLangs, targetLangs } = retrieveFiltered(source, target); - expect(sourceLangs.map(([code]) => code)).toStrictEqual(sourceKeys); - expect(targetLangs.map(([code]) => code)).toStrictEqual(targetKeys); + expect(sourceLangs).toStrictEqual(filteredEntries("source", target)); + expect(targetLangs).toStrictEqual(filteredEntries("target", source)); }); }); diff --git a/tests/utils/translate.test.ts b/tests/utils/translate.test.ts index 70a8c0a..e52c6f9 100644 --- a/tests/utils/translate.test.ts +++ b/tests/utils/translate.test.ts @@ -1,3 +1,4 @@ +import { htmlRes, resolveFetchWith } from "../commonUtils"; import faker from "faker"; import { googleScrape, extractSlug } from "../../utils/translate"; @@ -12,20 +13,15 @@ describe("googleScrape", () => { it("parses html response correctly", async () => { const translation = faker.random.words(); - const className = "result-container"; - const html = ` -
- ${translation} -
- `; - fetchMock.mockResponseOnce(async () => ({ body: html })); + const html = htmlRes(translation); + resolveFetchWith(html); expect(await googleScrape(source, target, query)).toStrictEqual({ translation }); }); it("returns status code on request error", async () => { const status = faker.random.number({ min: 400, max: 499 }); - fetchMock.mockResponseOnce(async () => ({ status })); + resolveFetchWith({ status }); expect(await googleScrape(source, target, query)).toStrictEqual({ statusCode: status }); }); @@ -40,12 +36,8 @@ describe("googleScrape", () => { it("returns correct message on parsing wrong class", async () => { const translation = faker.random.words(); const className = "wrong-container"; - const html = ` -
- ${translation} -
- `; - fetchMock.mockResponseOnce(async () => ({ body: html })); + const html = htmlRes(translation, className); + resolveFetchWith(html); const res = await googleScrape(source, target, query); expect(res?.errorMsg).toMatch(/parsing/); diff --git a/utils/language.ts b/utils/language.ts index c376c62..3719d52 100644 --- a/utils/language.ts +++ b/utils/language.ts @@ -3,16 +3,16 @@ import { languages, exceptions, mappings } from "./languages.json"; const checkTypes = { exception: exceptions, mapping: mappings -} +}; -type CheckType = keyof typeof checkTypes; +export type CheckType = keyof typeof checkTypes; const langTypes = [ "source", "target" ] as const; -type LangType = typeof langTypes[number]; +export type LangType = typeof langTypes[number]; const isKeyOf = (obj: T) => (key: keyof any): key is keyof T => key in obj;