Tests for translate utils
This commit is contained in:
@@ -7,11 +7,11 @@ module.exports = {
|
||||
'^.+\\.module\\.(css|sass|scss)$',
|
||||
],
|
||||
testMatch: [
|
||||
'<rootDir>/**/__tests__/**/*.{js,jsx,ts,tsx}',
|
||||
'<rootDir>/**/*.{spec,test}.{js,jsx,ts,tsx}',
|
||||
'<rootDir>/**/tests/*/**/*.{js,jsx,ts,tsx}',
|
||||
'<rootDir>/**/*.{spec,test}.{js,jsx,ts,tsx}'
|
||||
],
|
||||
setupFilesAfterEnv: [
|
||||
"<rootDir>/setupTests.ts"
|
||||
"<rootDir>/tests/setupTests.ts"
|
||||
],
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx"]
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@testing-library/jest-dom": "^5.11.9",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/faker": "^5.1.7",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^14.14.33",
|
||||
"@types/react": "^17.0.3",
|
||||
@@ -34,7 +35,9 @@
|
||||
"eslint-plugin-react": "^7.20.3",
|
||||
"eslint-plugin-react-hooks": "^4.0.8",
|
||||
"eslint-plugin-testing-library": "^3.9.0",
|
||||
"faker": "^5.4.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
@@ -44,6 +47,8 @@
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": ["next/babel"]
|
||||
"presets": [
|
||||
"next/babel"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import Languages from "../components/Languages";
|
||||
import { retrieveFiltered } from "../utils/language";
|
||||
import langReducer, { Actions, initialState } from "../utils/reducer";
|
||||
|
||||
const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ translation, error, initial }) => {
|
||||
const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ translation, statusCode, errorMsg, initial }) => {
|
||||
const [{ source, target, query }, dispatch] = useReducer(langReducer, initialState);
|
||||
const [encodedQuery, setEncodedQuery] = useState("");
|
||||
|
||||
@@ -40,8 +40,8 @@ const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ translation,
|
||||
|
||||
const { sourceLangs, targetLangs } = retrieveFiltered(source, target);
|
||||
|
||||
return error ? (
|
||||
<Error statusCode={error} />
|
||||
return statusCode ? (
|
||||
<Error statusCode={statusCode} />
|
||||
) : (
|
||||
<div>
|
||||
<Head>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import '@testing-library/jest-dom';
|
||||
@@ -1,4 +1,5 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import faker from "faker";
|
||||
import Error from "next/error";
|
||||
|
||||
it("renders a not found message on 404 code", () => {
|
||||
@@ -7,7 +8,7 @@ it("renders a not found message on 404 code", () => {
|
||||
});
|
||||
|
||||
it("renders the correct status code", () => {
|
||||
const code = Math.floor(Math.random() * 500) + 100;
|
||||
const code = faker.random.number({ min: 100, max: 599 });
|
||||
render(<Error statusCode={code} />);
|
||||
expect(screen.getByText(code)).toBeVisible();
|
||||
});
|
||||
3
tests/setupTests.ts
Normal file
3
tests/setupTests.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import jestFetchMock from 'jest-fetch-mock';
|
||||
jestFetchMock.enableMocks();
|
||||
75
tests/utils/translate.test.ts
Normal file
75
tests/utils/translate.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import faker from "faker";
|
||||
import { googleScrape, extractSlug } from "../../utils/translate";
|
||||
|
||||
const source = faker.random.locale();
|
||||
const target = faker.random.locale();
|
||||
const query = faker.random.words();
|
||||
|
||||
describe("googleScrape", () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
|
||||
it("parses html response correctly", async () => {
|
||||
const translation = faker.random.words();
|
||||
const className = "result-container";
|
||||
const html = `
|
||||
<div class=${className}>
|
||||
${translation}
|
||||
</div>
|
||||
`;
|
||||
fetchMock.mockResponseOnce(async () => ({ body: 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 }));
|
||||
|
||||
expect(await googleScrape(source, target, query)).toStrictEqual({ statusCode: status });
|
||||
});
|
||||
|
||||
it("returns correct message on network error", async () => {
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const res = await googleScrape(source, target, query);
|
||||
expect(res?.errorMsg).toMatch(/retrieving/);
|
||||
});
|
||||
|
||||
it("returns correct message on parsing wrong class", async () => {
|
||||
const translation = faker.random.words();
|
||||
const className = "wrong-container";
|
||||
const html = `
|
||||
<div class=${className}>
|
||||
${translation}
|
||||
</div>
|
||||
`;
|
||||
fetchMock.mockResponseOnce(async () => ({ body: html }));
|
||||
|
||||
const res = await googleScrape(source, target, query);
|
||||
expect(res?.errorMsg).toMatch(/parsing/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractSlug", () => {
|
||||
it("returns 'query' for 1 param", () => {
|
||||
expect(extractSlug([query])).toStrictEqual({ query });
|
||||
});
|
||||
|
||||
it("returns 'target' & 'query' resp. for 2 params", () => {
|
||||
expect(extractSlug([target, query])).toStrictEqual({ target, query });
|
||||
});
|
||||
|
||||
it("returns 'source', 'target' & 'query' resp. for 3 param", () => {
|
||||
expect(extractSlug([source, target, query])).toStrictEqual({ source, target, query });
|
||||
});
|
||||
|
||||
it("returns empty object on 0 or >4 params", () => {
|
||||
expect(extractSlug([])).toStrictEqual({});
|
||||
|
||||
const length = faker.random.number({ min: 4, max: 50 });
|
||||
const array = Array(length).fill("");
|
||||
expect(extractSlug(array)).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
@@ -7,20 +7,33 @@ export async function googleScrape(
|
||||
query: string
|
||||
): Promise<{
|
||||
translation?: string,
|
||||
error?: number
|
||||
statusCode?: number,
|
||||
errorMsg?: string
|
||||
}> {
|
||||
const parsed = replaceBoth("mapping", { source, target });
|
||||
const res = await fetch(`https://translate.google.com/m?sl=${parsed.source}&tl=${parsed.target}&q=${encodeURIComponent(query)}`);
|
||||
const res = await fetch(
|
||||
`https://translate.google.com/m?sl=${parsed.source}&tl=${parsed.target}&q=${encodeURIComponent(query)}`
|
||||
).catch(() => null);
|
||||
|
||||
if (!res)
|
||||
return {
|
||||
errorMsg: "An error occurred while retrieving the translation"
|
||||
}
|
||||
|
||||
if (!res.ok)
|
||||
return {
|
||||
error: res.status
|
||||
statusCode: res.status
|
||||
};
|
||||
|
||||
const html = await res.text();
|
||||
return {
|
||||
translation: cheerio.load(html)(".result-container").text()
|
||||
};
|
||||
const translation = cheerio.load(html)(".result-container").text().trim();
|
||||
|
||||
return translation
|
||||
? {
|
||||
translation
|
||||
} : {
|
||||
errorMsg: "An error occurred while parsing the translation"
|
||||
};
|
||||
}
|
||||
|
||||
export function extractSlug(slug: string[]): {
|
||||
@@ -33,10 +46,10 @@ export function extractSlug(slug: string[]): {
|
||||
case 1:
|
||||
return { query: p1 };
|
||||
case 2:
|
||||
return { target: p1, query: p2 }
|
||||
return { target: p1, query: p2 };
|
||||
case 3:
|
||||
return { source: p1, target: p2, query: p3 }
|
||||
return { source: p1, target: p2, query: p3 };
|
||||
default:
|
||||
return {}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
30
yarn.lock
30
yarn.lock
@@ -719,6 +719,11 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/faker@^5.1.7":
|
||||
version "5.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.1.7.tgz#1c3f0655f6e912f5578ce40baa941ab8705fc0ef"
|
||||
integrity sha512-ByseCEyhb+64fapaFR/yASUfAU+Ia4LpCnHhoNMQJFSIBvbtFoEZQB0elwrSF/idpKL5OZvmZhCCukGV8Zi22w==
|
||||
|
||||
"@types/graceful-fs@^4.1.2":
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
|
||||
@@ -1800,6 +1805,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
cross-fetch@^3.0.4:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c"
|
||||
integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==
|
||||
dependencies:
|
||||
node-fetch "2.6.1"
|
||||
|
||||
cross-spawn@^6.0.0:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
@@ -2586,6 +2598,11 @@ extsprintf@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
||||
|
||||
faker@^5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/faker/-/faker-5.4.0.tgz#f18e55993c6887918182b003d163df14daeb3011"
|
||||
integrity sha512-Y9n/Ky/xZx/Bj8DePvXspUYRtHl/rGQytoIT5LaxmNwSe3wWyOeOXb3lT6Dpipq240PVpeFaGKzScz/5fvff2g==
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
@@ -3480,6 +3497,14 @@ jest-environment-node@^26.6.2:
|
||||
jest-mock "^26.6.2"
|
||||
jest-util "^26.6.2"
|
||||
|
||||
jest-fetch-mock@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b"
|
||||
integrity sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==
|
||||
dependencies:
|
||||
cross-fetch "^3.0.4"
|
||||
promise-polyfill "^8.1.3"
|
||||
|
||||
jest-get-type@^26.3.0:
|
||||
version "26.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
|
||||
@@ -4759,6 +4784,11 @@ progress@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
promise-polyfill@^8.1.3:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0"
|
||||
integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==
|
||||
|
||||
prompts@^2.0.1:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7"
|
||||
|
||||
Reference in New Issue
Block a user