diff --git a/web/package.json b/web/package.json
index 8db99c8..1153141 100644
--- a/web/package.json
+++ b/web/package.json
@@ -24,6 +24,7 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
+ "test:coverage": "react-scripts test --coverage --watchAll false",
"eject": "react-scripts eject"
},
"jest": {
@@ -47,6 +48,7 @@
]
},
"devDependencies": {
+ "@testing-library/jest-dom": "^5.12.0",
"@types/axios": "^0.14.0",
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
diff --git a/web/src/__tests__/components/Input.spec.ts b/web/src/__tests__/components/Input.spec.ts
new file mode 100644
index 0000000..038d6cf
--- /dev/null
+++ b/web/src/__tests__/components/Input.spec.ts
@@ -0,0 +1,69 @@
+import React from 'react';
+
+import { render, fireEvent, wait } from '@testing-library/react';
+import Input from '../../components/Input';
+
+jest.mock('@unform/core', () => {
+ return {
+ useField() {
+ return {
+ fieldName: 'email',
+ defaultValue: '',
+ error: '',
+ registerField: jest.fn(),
+ };
+ },
+ };
+});
+
+describe('Input component', () => {
+ it('should be able to render an input', () => {
+ const { getByPlaceholderText } = render(
+ ,
+ );
+
+ expect(getByPlaceholderText('E-mail')).toBeTruthy();
+ });
+
+ it('should render highlight on input focus', async () => {
+ const { getByPlaceholderText, getByTestId } = render(
+ ,
+ );
+
+ const inputElement = getByPlaceholderText('E-mail');
+ const containerElement = getByTestId('input-container');
+
+ fireEvent.focus(inputElement);
+
+ await wait(() => {
+ expect(containerElement).toHaveStyle('border-color: #ff9000;');
+ expect(containerElement).toHaveStyle('color: #ff9000;');
+ });
+
+ fireEvent.blur(inputElement);
+
+ await wait(() => {
+ expect(containerElement).not.toHaveStyle('border-color: #ff9000;');
+ expect(containerElement).not.toHaveStyle('color: #ff9000;');
+ });
+ });
+
+ it('should keep input border highlight when input filled', async () => {
+ const { getByPlaceholderText, getByTestId } = render(
+ ,
+ );
+
+ const inputElement = getByPlaceholderText('E-mail');
+ const containerElement = getByTestId('input-container');
+
+ fireEvent.change(inputElement, {
+ target: { value: 'johndoe@example.com.br' },
+ });
+
+ fireEvent.blur(inputElement);
+
+ await wait(() => {
+ expect(containerElement).toHaveStyle('color: #ff9000;');
+ });
+ });
+});
diff --git a/web/src/__tests__/pages/SignIn.spec.tsx b/web/src/__tests__/pages/SignIn.spec.tsx
index 6dc994e..ba427ec 100644
--- a/web/src/__tests__/pages/SignIn.spec.tsx
+++ b/web/src/__tests__/pages/SignIn.spec.tsx
@@ -1,19 +1,98 @@
import React from 'react'
-import { render } from '@testing-library/react'
+import { render, fireEvent, wait } from '@testing-library/react'
import SignIn from '../../pages/SignIn'
+const mockedHistoryPush = jest.fn()
+const mockedSignIn = jest.fn()
+const mockedAddToast = jest.fn()
+
jest.mock('react-router-dom', () => {
return {
- useHistory: jest.fn(),
+ useHistory: () => ({
+ push: mockedHistoryPush
+ }),
Link: ({ children }: { children: React.ReactNode }) => children
}
})
+jest.mock('../../hooks/auth', () => {
+ return {
+ useAuth: () => ({
+ signIn: mockedSignIn
+ })
+ }
+})
+
+jest.mock('../../hooks/toast', () => {
+ return {
+ useToast: () => ({
+ addToast: mockedAddToast
+ })
+ }
+})
+
describe('SignIn Page', () => {
- it('should be able to sign in', () => {
- const { debug } = render()
+ beforeEach(() => {
+ mockedHistoryPush.mockClear()
+ })
+
+ it('should be able to sign in', async () => {
+ const { getByPlaceholderText, getByText } = render()
+
+ const emailField = getByPlaceholderText('E-mail')
+ const passwordField = getByPlaceholderText('Senha')
+ const buttonElement = getByText('Entrar')
+
+ fireEvent.change(emailField, { target: { value: 'johndoe@example.com' } })
+ fireEvent.change(passwordField, { target: { value: '123456' } })
+
+ fireEvent.click(buttonElement)
+
+ await wait(() => {
+ expect(mockedHistoryPush).toHaveBeenCalledWith('/dashboard')
+ })
+ })
+
+ it('should not be able to sign in with invalid credentials', async () => {
+ const { getByPlaceholderText, getByText } = render()
+
+ const emailField = getByPlaceholderText('E-mail')
+ const passwordField = getByPlaceholderText('Senha')
+ const buttonElement = getByText('Entrar')
+
+ fireEvent.change(emailField, { target: { value: 'not-valid-email' } })
+ fireEvent.change(passwordField, { target: { value: '123456' } })
+
+ fireEvent.click(buttonElement)
+
+ await wait(() => {
+ expect(mockedHistoryPush).not.toHaveBeenCalled()
+ })
+ })
+
+ it('should display an error if login fails', async () => {
+ mockedSignIn.mockImplementation(() => {
+ throw new Error()
+ })
+
+ const { getByPlaceholderText, getByText } = render()
+
+ const emailField = getByPlaceholderText('E-mail')
+ const passwordField = getByPlaceholderText('Senha')
+ const buttonElement = getByText('Entrar')
+
+ fireEvent.change(emailField, { target: { value: 'johndoe@example.com' } })
+ fireEvent.change(passwordField, { target: { value: '123456' } })
+
+ fireEvent.click(buttonElement)
- debug()
+ await wait(() => {
+ expect(mockedAddToast).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'error'
+ })
+ )
+ })
})
})
diff --git a/web/src/pages/SignIn/index.tsx b/web/src/pages/SignIn/index.tsx
index 0d439ff..cd9b41f 100644
--- a/web/src/pages/SignIn/index.tsx
+++ b/web/src/pages/SignIn/index.tsx
@@ -115,4 +115,5 @@ const SingIn: React.FC = () => {
);
}
+
export default SingIn;
diff --git a/web/yarn.lock b/web/yarn.lock
index a3f0a38..74a15d8 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1095,7 +1095,7 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.10.2":
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.9.2":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
@@ -1517,6 +1517,20 @@
lz-string "^1.4.4"
pretty-format "^26.6.2"
+"@testing-library/jest-dom@^5.12.0":
+ version "5.12.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.12.0.tgz#6a5d340b092c44b7bce17a4791b47d9bc2c61443"
+ integrity sha512-N9Y82b2Z3j6wzIoAqajlKVF1Zt7sOH0pPee0sUHXHc5cv2Fdn23r+vpWm0MBBoGJtPOly5+Bdx1lnc3CD+A+ow==
+ dependencies:
+ "@babel/runtime" "^7.9.2"
+ "@types/testing-library__jest-dom" "^5.9.1"
+ aria-query "^4.2.2"
+ chalk "^3.0.0"
+ css "^3.0.0"
+ css.escape "^1.5.1"
+ lodash "^4.17.15"
+ redent "^3.0.0"
+
"@testing-library/react@^11.2.6":
version "11.2.6"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b"
@@ -1623,7 +1637,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@^26.0.23":
+"@types/jest@*", "@types/jest@^26.0.23":
version "26.0.23"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7"
integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==
@@ -1730,6 +1744,13 @@
"@types/react" "*"
csstype "^3.0.2"
+"@types/testing-library__jest-dom@^5.9.1":
+ version "5.9.5"
+ resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0"
+ integrity sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==
+ dependencies:
+ "@types/jest" "*"
+
"@types/uuid@^8.3.0":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
@@ -3092,6 +3113,14 @@ chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
+chalk@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+ integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
chalk@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
@@ -3677,6 +3706,11 @@ css-what@^3.2.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
+css.escape@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
+ integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
+
css@^2.0.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
@@ -3687,6 +3721,15 @@ css@^2.0.0:
source-map-resolve "^0.5.2"
urix "^0.1.0"
+css@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
+ integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
+ dependencies:
+ inherits "^2.0.4"
+ source-map "^0.6.1"
+ source-map-resolve "^0.6.0"
+
cssdb@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
@@ -7252,6 +7295,11 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+min-indent@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
+ integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
+
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
@@ -9357,6 +9405,14 @@ recursive-readdir@2.2.2:
dependencies:
minimatch "3.0.4"
+redent@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
+ integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
+ dependencies:
+ indent-string "^4.0.0"
+ strip-indent "^3.0.0"
+
regenerate-unicode-properties@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
@@ -10083,6 +10139,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"
+source-map-resolve@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
+ integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
+ dependencies:
+ atob "^2.1.2"
+ decode-uri-component "^0.2.0"
+
source-map-support@^0.5.6, source-map-support@~0.5.12:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@@ -10396,6 +10460,13 @@ strip-eof@^1.0.0:
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+strip-indent@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
+ integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
+ dependencies:
+ min-indent "^1.0.0"
+
strip-json-comments@^3.0.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"