diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..85f78eb4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#142F4C", + "titleBar.activeBackground": "#1B426A", + "titleBar.activeForeground": "#F8FAFD" + } +} \ No newline at end of file diff --git a/README.md b/README.md index 16ae2fdc..fa04bd35 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,19 @@ This module explored the basics of the react-testing library and ideas behind the integration testing methodology. In this project you will practice how to build tests that follow the arrange-act-assert model, different methods of querying for DOM elements in a test, the use of different expect types and using async/await to test for changes in state. +- [Integration Testing React Module Project : Contact Form - Testing](#integration-testing-react-module-project--contact-form---testing) + - [Testing Web Applications](#testing-web-applications) + - [Objectives](#objectives) + - [Introduction](#introduction) + - [Instructions](#instructions) + - [Task 1: Project Set Up](#task-1-project-set-up) + - [Task 2: Project Requirements](#task-2-project-requirements) + - [Test Brainstorming](#test-brainstorming) + - [Complete a case that tests if:](#complete-a-case-that-tests-if) + - [Stretch goals](#stretch-goals) + - [Rick Mansfield's PUll Request Link](#rick-mansfields-pull-request-link) + - [Resources](#resources) + ## Testing Web Applications ## Objectives @@ -56,3 +69,13 @@ As a developer, you will be writing tests for every component. As we've learned, - Look at your test cases in Understanding-questions and see if there are any that you have not completed. - From the this list or from your own mind, add in at least one more new testcase. - There is alot of state management within our component in this project! See if you can separate the form and error validation code into their own hooks. + + +## Rick Mansfield's PUll Request Link + +- [Link for Convenience](https://github.com/LambdaSchool/web-module-project-testing-web-apps/pull/71) + +## Resources + - [React Testing Library Cheatsheet](https://testing-library.com/docs/react-testing-library/cheatsheet/) + - [Regex](https://regexr.com) + - [jest](https://jestjs.io/docs/en/expect.html) \ No newline at end of file diff --git a/notes.md b/notes.md new file mode 100644 index 00000000..63e83cec --- /dev/null +++ b/notes.md @@ -0,0 +1,42 @@ +this file contains notes from the video regarding options on waiting for a promist to be completed. Use the preview for an easier read. +```javascript +test("User can add multiple animals", async () => { + // Arrange: render & grab the elements we need + render(); + + const speciesInput = screen.getByLabelText(/species/i); + const ageInput = screen.getByLabelText(/age/i); + const notesInput = screen.getByLabelText(/notes/i); + const submitButton = screen.getByRole('button', { name: /submit/i }); + + // Act: fill out the form and click the button (simulating user behavior with userEvent) + userEvent.type(speciesInput, "Deer"); + userEvent.type(ageInput, "98"); + userEvent.type(notesInput, "I'm the first animal and I love 98 Degrees"); + userEvent.click(submitButton); + + // Intermediate assertion: now we should have just deer, no llamas + expect(screen.getByText(/deer/i)).toBeInTheDocument(); + expect(screen.queryByText(/llama/i)).toEqual(null); + + //async assertion: + + // if you are changing state you may want to "wait" for the change... such as a change handler + //THE PROMISE WAY ... + // const newItemPromise = screen.findByText("deer"); + // console.log(newItemPromise); + // newItemPromise.then(newItem=>{ + // console.log(newItem); + // expect(newItem).toBeInTheDocument(); + // }); + + //THE AWAIT WAY + // 1) + // const newItem = await screen.findByText("deer");//did you put "async" in first line? + // console.log(newItem); + // 2) + await waitFor(() => { + const newItem = screen.findByText("deer"); + expect(newItem).toBeInTheDocument(); + }); + ``` diff --git a/src/components/ContactForm.test.js b/src/components/ContactForm.test.js index 5f80a897..fe32dde3 100644 --- a/src/components/ContactForm.test.js +++ b/src/components/ContactForm.test.js @@ -4,38 +4,155 @@ import userEvent from '@testing-library/user-event'; import ContactForm from './ContactForm'; -test('renders without errors', ()=>{ - +test("does the test 1", ()=>{ + // throw new Error("This is an error") + console.log('doing a sanity test 1'); }); -test('renders the contact form header', ()=> { - + +it('renders without errors', ()=>{ + render(); }); -test('renders ONE error message if user enters less then 5 characters into firstname.', async () => { - +test('2 renders the contact form header', ()=> { + render(); + const header = screen.queryByText(/contact form/i); + console.log(header); + expect(header).toBeInTheDocument();//can use either + expect(header).toBeVisible();//can use any of these + expect(header).toHaveTextContent(/contact form/i); + expect(header).toBeTruthy(); + expect(header).not.toBeFalsy(); +//another example to just have plenty of options + const h1 = screen.queryByTestId('testh1'); + expect(h1).toBeInTheDocument();//can also tag with an id and use any of these }); -test('renders THREE error messages if user enters no values into any fields.', async () => { - + +test('3 renders ONE error message if user enters less then 5 characters into firstname.', async () => { + render(); + const firstName = "Ric"; + const firstNameInput = screen.getByLabelText(/first name/i); + userEvent.type(firstNameInput, firstName); + const errorMessage = screen.queryAllByText(/error/i); + expect(errorMessage).toHaveLength(1); + expect(errorMessage).toBeTruthy();//same thing }); -test('renders ONE error message if user enters a valid first name and last name but no email.', async () => { - + +test('4 renders THREE error messages if user enters no values into any fields.', async () => { + render(); + const submitButton = screen.getByRole('button');//arrange + userEvent.click(submitButton); + const errorMessages = screen.queryAllByTestId(/error/i); + expect(errorMessages).toHaveLength(3); + }); -test('renders "email must be a valid email address" if an invalid email is entered', async () => { - +test('5 renders ONE error message if user enters a valid first name and last name but no email.', async () => { + render(); + //enters first name + const firstName = "Ricster"; + const firstNameInput = screen.getByLabelText(/first name/i); + userEvent.type(firstNameInput, firstName); + //enters last name + const lastName = "Mansfield"; + const lastNameInput = screen.getByLabelText(/Last Name/i); + userEvent.type(lastNameInput, lastName); + //uses submit button before entering email + const button = screen.getByRole('button'); + userEvent.click(button); + //should see error for email + const errorMessages = screen.queryAllByText(/error/i); + expect(errorMessages).toHaveLength(1); + //alternative check for email error message using preestablished variable from test 4 + const emailErrorMessage = screen.queryAllByTestId(/error/i); + expect(emailErrorMessage).toHaveLength(1); + }); -test('renders "lastName is a required field" if an last name is not entered and the submit button is clicked', async () => { - +test('6 renders "email must be a valid email address" if an invalid email is entered', async () => { + render(); + //Arrange = create bad email and retrieve email input field + const email = 'badEmail'; + const emailInput = screen.getByLabelText(/email/i); + //Act = input bad email into field just like a user might do & use submitt button + userEvent.type(emailInput, email); + const submitButton = screen.getByRole('button');//arrange + userEvent.click(submitButton); + //Assert = expect to get error message for email + const errorMessage = screen.queryByText(/email must be a valid email address/i); + expect(errorMessage).toBeVisible(); + }); -test('renders all firstName, lastName and email text when submitted. Does NOT render message if message is not submitted.', async () => { +test('7 renders "lastName is a required field" if an last name is not entered and the submit button is clicked', async () => { + render(); + //ARRANGE - The message prints upon using submit without doing anything So We only need to screen for the button + const submitButton = screen.getByRole('button'); + //ACT - user then clicks the button + userEvent.click(submitButton); + //ASSERT - error message for last name is expected + const errorMessage = screen.queryByText(/lastName is a required field/i); + expect(errorMessage).toBeVisible(); }); -test('renders all fields text when all fields are submitted.', async () => { +test('8 renders all firstName, lastName and email text when submitted. Does NOT render message if message is not submitted.', async () => { + + render(); + //Arrange Need good first, last and email and screen for each field + const firstName = 'William'; + const lastName = 'Mansfield'; + const email = 'RicksMyCodeGuy@gmail.com'; + + const firstNameInput = screen.getByLabelText(/first name/i); + const lastNameInput = screen.getByLabelText(/Last Name/i); + const emailInput = screen.getByLabelText(/email/i); + //Act user inputs each and clicks submit button no message until after clicked + userEvent.type(firstNameInput, firstName); + userEvent.type(lastNameInput, lastName); + userEvent.type(emailInput, email); + + const messageDiv = screen.queryByText(/you submitted/i); + expect(messageDiv).toBeFalsy();//starts falsy + + const button = screen.getByRole('button'); + userEvent.click(button); + //Assert - should get affimative You Submitted: card back with all details rendered back + // expect(messageDiv).toBeTruthy();//ends truthy couldn't get this to work. seeing help + + const firstNameDisplay = screen.queryByTestId('firstnameDisplay'); + const lastNameDisplay = screen.queryByTestId('lastnameDisplay'); + const emailDisplay = screen.queryByTestId('emailDisplay'); + + expect(firstNameDisplay).toBeVisible(); + expect(lastNameDisplay).toBeVisible(); + expect(emailDisplay).toBeVisible(); + + test('9 renders all fields text when all fields are submitted.', async () => { + render(); + //Arranged need good first, last, email, scan for each field + const firstName = 'Wiliam'; + const lastName = 'Mansfield'; + const email = 'RicksMyCodeGuy@gmail.com'; + + const firstNameInput = screen.getByLabelText(/first name/i); + const lastNameInput = screen.getByLabelText(/last name/i); + const emailInput = screen.getByLabelText(/email/i); + + //ACT - user enters each as arranged above and uses submit button + userEvent.type(firstNameInput, firstName); + userEvent.type(lastNameInput, lastName); + userEvent.type(emailInput, email); + + const button = screen.getByRole('button'); + userEvent.click(button); + + //Assert - must utilize a delay method | Per Warrens video the "Await" vs the "Promise" is shorter. I've included the notes from class an an extra file called notes.md attached next to the readme.md file. + const firstNameDisplay = await screen.findByTestId('firstnameDisplay'); -}); \ No newline at end of file + expect(firstNameDisplay).toBeVisible(); + expect(screen.getByText(/mansfield/i)).toBeTruthy(); + screen.getByText(/mansfield/i); + }); \ No newline at end of file