APIs (#3)
* Initial RESTful API * RESTful API tests * Scrapping error handling refactored * Initial GraphQL API * GraphQL API tests
This commit is contained in:
@@ -2,8 +2,11 @@ import { render, screen } from "../reactUtils";
|
||||
import faker from "faker";
|
||||
import CustomError from "../../components/CustomError";
|
||||
|
||||
const code = faker.random.number({ min: 400, max: 599 });
|
||||
const text = faker.random.words();
|
||||
|
||||
it("loads the layout correctly", async () => {
|
||||
render(<CustomError statusCode={404} />);
|
||||
render(<CustomError statusCode={code} statusText={text} />);
|
||||
|
||||
expect(screen.getByRole("link", { name: /skip to content/i })).toBeEnabled();
|
||||
expect(await screen.findByRole("img", { name: /logo/i })).toBeVisible();
|
||||
@@ -12,13 +15,10 @@ it("loads the layout correctly", async () => {
|
||||
expect(screen.getByText(/\xA9/)).toBeVisible();
|
||||
});
|
||||
|
||||
it("renders a not found message on 404 code", () => {
|
||||
render(<CustomError statusCode={404} />);
|
||||
expect(screen.getByText(/this page could not be found/i)).toBeVisible();
|
||||
});
|
||||
|
||||
it("renders the correct status code", () => {
|
||||
it("renders the correct status code & text", () => {
|
||||
const code = faker.random.number({ min: 400, max: 599 });
|
||||
render(<CustomError statusCode={code} />);
|
||||
render(<CustomError statusCode={code} statusText={text} />);
|
||||
|
||||
expect(screen.getByText(code)).toBeVisible();
|
||||
expect(screen.getByText(text)).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -173,12 +173,6 @@ describe("Page", () => {
|
||||
expect(btnCopy).toBeEnabled();
|
||||
});
|
||||
|
||||
it("renders error page on status code", async () => {
|
||||
const code = faker.random.number({ min: 400, max: 599 });
|
||||
render(<Page statusCode={code} />);
|
||||
await waitFor(() => expect(screen.getByText(code)).toBeVisible());
|
||||
});
|
||||
|
||||
it("shows alert correctly on error", async () => {
|
||||
const errorMsg = faker.random.words();
|
||||
render(<Page errorMsg={errorMsg} />);
|
||||
|
||||
179
tests/pages/api/graphql.test.ts
Normal file
179
tests/pages/api/graphql.test.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { createTestClient } from "apollo-server-testing";
|
||||
import { ApolloServer } from "apollo-server-micro";
|
||||
import faker from "faker";
|
||||
import { htmlRes, resolveFetchWith } from "../../commonUtils";
|
||||
import { typeDefs, resolvers } from "../../../pages/api/graphql";
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
|
||||
const { query } = createTestClient(new ApolloServer({ typeDefs, resolvers }));
|
||||
|
||||
it("doesn't trigger fetch if neither target nor audio are specified", async () => {
|
||||
const text = faker.random.words();
|
||||
const { data } = await query({
|
||||
query: `
|
||||
query($text: String!) {
|
||||
translation(query: $text) {
|
||||
source {
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { text }
|
||||
});
|
||||
expect(data).toMatchObject({ translation: { source: { text } } });
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns translation triggering fetch", async () => {
|
||||
const text = faker.random.words();
|
||||
const translation = faker.random.words();
|
||||
resolveFetchWith(htmlRes(translation));
|
||||
|
||||
const { data } = await query({
|
||||
query: `
|
||||
query($text: String!) {
|
||||
translation(query: $text) {
|
||||
target {
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { text }
|
||||
});
|
||||
expect(data).toMatchObject({ translation: { target: { text: translation } } });
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("returns audio triggering fetch", async () => {
|
||||
const lang = faker.random.locale();
|
||||
const text = faker.random.words();
|
||||
resolveFetchWith({ status: 200 });
|
||||
|
||||
const { data } = await query({
|
||||
query: `
|
||||
query($lang: String! $text: String!) {
|
||||
audio(lang: $lang query: $text) {
|
||||
lang
|
||||
text
|
||||
audio
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { lang, text }
|
||||
});
|
||||
expect(data).toMatchObject({ audio: { lang, text, audio: expect.any(Array) } });
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("returns null on translation error", async () => {
|
||||
const text = faker.random.words();
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const { data } = await query({
|
||||
query: `
|
||||
query($text: String!) {
|
||||
translation(query: $text) {
|
||||
target {
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { text }
|
||||
});
|
||||
expect(data).toMatchObject({ translation: { target: { text: null } } });
|
||||
});
|
||||
|
||||
it("returns null on audio error", async () => {
|
||||
const lang = faker.random.locale();
|
||||
const text = faker.random.words();
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const { data } = await query({
|
||||
query: `
|
||||
query($lang: String! $text: String!) {
|
||||
audio(lang: $lang query: $text) {
|
||||
audio
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { lang, text }
|
||||
});
|
||||
expect(data).toMatchObject({ audio: { audio: null } });
|
||||
});
|
||||
|
||||
it("keeps a default value for both source and target languages", async () => {
|
||||
const text = faker.random.words();
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const { data } = await query({
|
||||
query: `
|
||||
query($text: String!) {
|
||||
translation(query: $text) {
|
||||
source {
|
||||
lang
|
||||
}
|
||||
target {
|
||||
lang
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { text }
|
||||
});
|
||||
expect(data).toMatchObject({ translation: { source: { lang: "auto" }, target: { lang: "en" } } });
|
||||
});
|
||||
|
||||
it("throws error on empty query in translation", async () => {
|
||||
const { errors } = await query({
|
||||
query: `
|
||||
query {
|
||||
translation {
|
||||
source {
|
||||
lang
|
||||
}
|
||||
target {
|
||||
lang
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
});
|
||||
expect(errors).toBeTruthy();
|
||||
});
|
||||
|
||||
it("throws error on empty lang or query in audio", async () => {
|
||||
const lang = faker.random.locale();
|
||||
const text = faker.random.words();
|
||||
|
||||
const { errors: queryErrors } = await query({
|
||||
query: `
|
||||
query($lang: String!) {
|
||||
audio(lang: $lang) {
|
||||
lang
|
||||
text
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { lang }
|
||||
});
|
||||
expect(queryErrors).toBeTruthy();
|
||||
|
||||
const { errors: langErrors } = await query({
|
||||
query: `
|
||||
query($text: String!) {
|
||||
audio(query: $text) {
|
||||
lang
|
||||
text
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { text }
|
||||
});
|
||||
expect(langErrors).toBeTruthy();
|
||||
});
|
||||
95
tests/pages/api/v1/[[...slug]].test.ts
Normal file
95
tests/pages/api/v1/[[...slug]].test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import httpMocks from "node-mocks-http";
|
||||
import faker from "faker";
|
||||
import { htmlRes, resolveFetchWith } from "../../../commonUtils";
|
||||
import handler from "../../../../pages/api/v1/[[...slug]]";
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
|
||||
const source = faker.random.locale();
|
||||
const target = faker.random.locale();
|
||||
const query = faker.random.words();
|
||||
const slug = [source, target, query];
|
||||
|
||||
it("returns 404 on <3 params", async () => {
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "GET",
|
||||
query: { slug: [source, target] }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it("returns 404 on >3 params", async () => {
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "GET",
|
||||
query: { slug: [source, target, query, ""] }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it("returns 405 on forbidden method", async () => {
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "POST",
|
||||
query: { slug }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(405);
|
||||
});
|
||||
|
||||
it("returns translation on scrapping resolve", async () => {
|
||||
const translationRes = faker.random.words();
|
||||
resolveFetchWith(htmlRes(translationRes));
|
||||
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "GET",
|
||||
query: { slug }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res._getJSONData()).toStrictEqual({ translation: translationRes });
|
||||
});
|
||||
|
||||
it("returns 500 on scrapping error", async () => {
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "GET",
|
||||
query: { slug }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(500);
|
||||
expect(res._getJSONData()).toStrictEqual({ error: expect.any(String) });
|
||||
});
|
||||
|
||||
it("returns audio on audio request", async () => {
|
||||
resolveFetchWith({ status: 200 });
|
||||
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "GET",
|
||||
query: { slug: ["audio", target, query] }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res._getJSONData()).toStrictEqual({ audio: expect.any(Array) });
|
||||
});
|
||||
|
||||
it("returns 500 on audio request error", async () => {
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const { req, res } = httpMocks.createMocks<any, any>({
|
||||
method: "GET",
|
||||
query: { slug: ["audio", target, query] }
|
||||
});
|
||||
|
||||
await handler(req, res);
|
||||
expect(res.statusCode).toBe(500);
|
||||
});
|
||||
@@ -19,11 +19,12 @@ describe("googleScrape", () => {
|
||||
expect(await googleScrape(source, target, query)).toStrictEqual({ translationRes });
|
||||
});
|
||||
|
||||
it("returns status code on request error", async () => {
|
||||
it("returns correct message on request error", async () => {
|
||||
const status = faker.random.number({ min: 400, max: 499 });
|
||||
resolveFetchWith({ status });
|
||||
|
||||
expect(await googleScrape(source, target, query)).toStrictEqual({ statusCode: status });
|
||||
const res = await googleScrape(source, target, query);
|
||||
expect(res?.errorMsg).toMatch(/retrieving/);
|
||||
});
|
||||
|
||||
it("returns correct message on network error", async () => {
|
||||
|
||||
Reference in New Issue
Block a user