From dcca881f1bae7234427d7e0f72886dd4c61eb1c3 Mon Sep 17 00:00:00 2001 From: Liam Stevens <8955671+liamstevens111@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:05:36 +0700 Subject: [PATCH] [#21] Use nock for mocking API response WIP --- package-lock.json | 43 +++++++++ package.json | 1 + src/routes/index.tsx | 2 +- src/screens/Login/index.test.tsx | 91 +++++++++++++++++++ .../{Home/login.tsx => Login/index.tsx} | 4 +- 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 src/screens/Login/index.test.tsx rename src/screens/{Home/login.tsx => Login/index.tsx} (96%) diff --git a/package-lock.json b/package-lock.json index 3ed68f9..62bc580 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "danger": "10.9.0", "danger-plugin-istanbul-coverage": "1.6.2", "eslint": "8.11.0", + "nock": "^13.3.0", "postcss": "8.4.21", "postcss-import": "14.1.0", "prettier": "2.6.0", @@ -16753,6 +16754,21 @@ "tslib": "^2.0.3" } }, + "node_modules/nock": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.0.tgz", + "integrity": "sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, "node_modules/node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -19339,6 +19355,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -36708,6 +36733,18 @@ "tslib": "^2.0.3" } }, + "nock": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.0.tgz", + "integrity": "sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + } + }, "node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -38374,6 +38411,12 @@ } } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", diff --git a/package.json b/package.json index d32cd1c..5b9e18a 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "danger": "10.9.0", "danger-plugin-istanbul-coverage": "1.6.2", "eslint": "8.11.0", + "nock": "13.3.0", "postcss": "8.4.21", "postcss-import": "14.1.0", "prettier": "2.6.0", diff --git a/src/routes/index.tsx b/src/routes/index.tsx index ec00d40..2d93c7f 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { RouteObject } from 'react-router-dom'; import HomeScreen from 'screens/Home'; -import LoginScreen from 'screens/Home/login'; +import LoginScreen from 'screens/Login'; const routes: RouteObject[] = [ { diff --git a/src/screens/Login/index.test.tsx b/src/screens/Login/index.test.tsx new file mode 100644 index 0000000..8692fd5 --- /dev/null +++ b/src/screens/Login/index.test.tsx @@ -0,0 +1,91 @@ +import { BrowserRouter } from 'react-router-dom'; + +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import nock from 'nock'; + +import LoginScreen from '.'; + +afterAll(() => { + nock.cleanAll(); + nock.restore(); +}); + +describe('LoginScreen', () => { + test('submit an an empty email and password and receive errors', async () => { + render(, { wrapper: BrowserRouter }); + + const submitButton = screen.getByRole('button', { name: 'login.sign-in' }); + + await userEvent.click(submitButton); + + expect(screen.getByText('login.invalid-email')).toBeInTheDocument(); + expect(screen.getByText('login.invalid-password')).toBeInTheDocument(); + }); + + test('submit incorrect details', async () => { + render(, { wrapper: BrowserRouter }); + + const formData = { + email: 'testemail@gmail.com', + password: 'password123', + }; + + nock(`${process.env.REACT_APP_API_ENDPOINT}`) + .defaultReplyHeaders({ + 'access-control-allow-origin': '*', + 'access-control-allow-credentials': 'true', + }) + .post('/oauth/token', { + email: formData.email, + password: formData.password, + grant_type: 'password', + client_id: process.env.REACT_APP_API_CLIENT_ID, + client_secret: process.env.REACT_APP_API_CLIENT_SECRET, + }) + .reply(400, { + errors: [ + { + detail: 'Your email or password is incorrect. Please try again.', + code: 'invalid_email_or_password', + }, + ], + }); + + const emailField = screen.getByLabelText('login.email'); + const passwordField = screen.getByLabelText('login.password'); + const submitButton = screen.getByRole('button', { name: 'login.sign-in' }); + + await userEvent.type(emailField, formData.email); + await userEvent.type(passwordField, formData.password); + + await userEvent.click(submitButton); + await waitFor(() => { + expect(screen.getByText('Your email or password is incorrect. Please try again.')).toBeInTheDocument(); + }); + }); + + // test('Allows form submission of email and password', async () => { + // const requestData = { + // email: 'testemail@gmail.com', + // password: 'password123', + // }; + + // render(, { wrapper: BrowserRouter }); + + // const emailField = screen.getByLabelText('login.email'); + // const passwordField = screen.getByLabelText('login.password'); + // const submitButton = screen.getByRole('button', { name: 'login.sign-in' }); + + // await userEvent.type(emailField, 'testemail@gmail.com'); + // await userEvent.type(passwordField, 'password123'); + + // const requestSpy = jest.spyOn(axios, 'request').mockImplementation(() => Promise.resolve({})); + + // await userEvent.click(submitButton); + + // expect(axios.request).toHaveBeenCalledWith(requestData); + + // requestSpy.mockRestore(); + // }); +}); diff --git a/src/screens/Home/login.tsx b/src/screens/Login/index.tsx similarity index 96% rename from src/screens/Home/login.tsx rename to src/screens/Login/index.tsx index 8a7dc73..1b1be89 100644 --- a/src/screens/Home/login.tsx +++ b/src/screens/Login/index.tsx @@ -41,10 +41,10 @@ function LoginScreen() { setToken({ accessToken: accessToken, refreshToken: refreshToken }); navigate('/'); } catch (error) { - let errorMessage = ''; + let errorMessage = 'There was a problem receiving a response from the server'; if (error instanceof Error) { - errorMessage = (error as AxiosError).response?.data?.errors[0]?.detail || error.message || 'Internal error'; + errorMessage = (error as AxiosError).response?.data?.errors[0]?.detail || error.cause || errorMessage; } setErrors([errorMessage]); } finally {