├── .gitignore ├── README.md ├── attachments ├── 01-getting-started-starting-project.zip ├── 02-basics-starting-project.zip ├── 03-diving-deeper-starting-project.zip ├── 04-config-starting-project.zip ├── 05-stubs-starting-project.zip └── 06-network-auth-starting-project.zip ├── code ├── 01 Getting Started │ ├── 01 Starting Project │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── react.svg │ │ │ ├── components │ │ │ │ ├── CourseGoal.jsx │ │ │ │ ├── CourseGoal.module.css │ │ │ │ ├── CourseGoals.jsx │ │ │ │ ├── CourseGoals.module.css │ │ │ │ └── Header.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ └── 02 Finished First Test │ │ ├── cypress.config.js │ │ ├── cypress │ │ ├── e2e │ │ │ └── first-test.cy.js │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.jsx │ │ ├── components │ │ │ ├── CourseGoal.jsx │ │ │ ├── CourseGoal.module.css │ │ │ ├── CourseGoals.jsx │ │ │ ├── CourseGoals.module.css │ │ │ └── Header.jsx │ │ ├── index.css │ │ └── main.jsx │ │ └── vite.config.js ├── 02 Basics │ ├── 01 Starting Project │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 02 Added Cypress │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── basics.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 03 Selecting By Text │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── basics.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 04 Implicit vs Explicit Assertions │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── basics.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 05 get() vs find() │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── basics.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 06 Simulating User Interaction │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── basics.cy.js │ │ │ │ └── tasks.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 07 Simulating Keyboard Typing │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── basics.cy.js │ │ │ │ └── tasks.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ ├── 08 Selecting Dropdown Values │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── basics.cy.js │ │ │ │ └── tasks.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ ├── Filter.jsx │ │ │ │ ├── Header.css │ │ │ │ ├── Header.jsx │ │ │ │ ├── Modal.css │ │ │ │ ├── Modal.jsx │ │ │ │ ├── NewTask.css │ │ │ │ ├── NewTask.jsx │ │ │ │ ├── Task.css │ │ │ │ ├── Task.jsx │ │ │ │ ├── TaskControl.css │ │ │ │ ├── TaskControl.jsx │ │ │ │ ├── TaskList.css │ │ │ │ └── TaskList.jsx │ │ │ ├── index.css │ │ │ └── main.jsx │ │ └── vite.config.js │ └── 09 Time For More Queries │ │ ├── cypress.config.js │ │ ├── cypress │ │ ├── e2e │ │ │ ├── basics.cy.js │ │ │ └── tasks.cy.js │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.jsx │ │ ├── assets │ │ │ └── logo.png │ │ ├── components │ │ │ ├── Filter.jsx │ │ │ ├── Header.css │ │ │ ├── Header.jsx │ │ │ ├── Modal.css │ │ │ ├── Modal.jsx │ │ │ ├── NewTask.css │ │ │ ├── NewTask.jsx │ │ │ ├── Task.css │ │ │ ├── Task.jsx │ │ │ ├── TaskControl.css │ │ │ ├── TaskControl.jsx │ │ │ ├── TaskList.css │ │ │ └── TaskList.jsx │ │ ├── index.css │ │ └── main.jsx │ │ └── vite.config.js ├── 03 Diving Deeper │ ├── 01 Starting Project │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 02 More on Selecting Elements │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 03 More Assertions │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 04 Working with Values & Aliases │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 05 Getting More Direct Element Access │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 06 Simulating Special Key Presses │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 07 Changing Subjects │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 08 Screenshots │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ └── 09 should instead of then │ │ ├── cypress.config.js │ │ ├── cypress │ │ ├── e2e │ │ │ ├── contact.cy.js │ │ │ └── navigation.cy.js │ │ ├── fixtures │ │ │ └── example.json │ │ ├── screenshots │ │ │ └── contact.cy.js │ │ │ │ ├── contact form -- should submit the form (1).png │ │ │ │ └── contact form -- should submit the form.png │ │ ├── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ │ └── videos │ │ │ ├── contact.cy.js.mp4 │ │ │ └── navigation.cy.js.mp4 │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.jsx │ │ ├── components │ │ │ ├── ContactForm.jsx │ │ │ ├── ContactForm.module.css │ │ │ ├── Header.jsx │ │ │ └── Header.module.css │ │ ├── index.css │ │ ├── main.jsx │ │ └── pages │ │ │ ├── About.jsx │ │ │ └── Home.jsx │ │ └── vite.config.js ├── 04 Configuration │ ├── 01 Starting Project │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ ├── screenshots │ │ │ │ └── contact.cy.js │ │ │ │ │ ├── contact form -- should submit the form (1).png │ │ │ │ │ └── contact form -- should submit the form.png │ │ │ ├── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ │ └── videos │ │ │ │ ├── contact.cy.js.mp4 │ │ │ │ └── navigation.cy.js.mp4 │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ ├── 02 Hooks │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ ├── contact.cy.js │ │ │ │ └── navigation.cy.js │ │ │ ├── fixtures │ │ │ │ └── example.json │ │ │ ├── screenshots │ │ │ │ └── contact.cy.js │ │ │ │ │ ├── contact form -- should submit the form (1).png │ │ │ │ │ └── contact form -- should submit the form.png │ │ │ ├── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ │ └── videos │ │ │ │ ├── contact.cy.js.mp4 │ │ │ │ └── navigation.cy.js.mp4 │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── App.jsx │ │ │ ├── components │ │ │ │ ├── ContactForm.jsx │ │ │ │ ├── ContactForm.module.css │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.module.css │ │ │ ├── index.css │ │ │ ├── main.jsx │ │ │ └── pages │ │ │ │ ├── About.jsx │ │ │ │ └── Home.jsx │ │ └── vite.config.js │ └── 03 Finished │ │ ├── cypress.config.js │ │ ├── cypress │ │ ├── e2e │ │ │ ├── contact.cy.js │ │ │ └── navigation.cy.js │ │ ├── fixtures │ │ │ └── example.json │ │ ├── screenshots │ │ │ └── contact.cy.js │ │ │ │ ├── contact form -- should submit the form (1).png │ │ │ │ └── contact form -- should submit the form.png │ │ ├── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ │ └── videos │ │ │ ├── contact.cy.js.mp4 │ │ │ └── navigation.cy.js.mp4 │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.jsx │ │ ├── components │ │ │ ├── ContactForm.jsx │ │ │ ├── ContactForm.module.css │ │ │ ├── Header.jsx │ │ │ └── Header.module.css │ │ ├── index.css │ │ ├── main.jsx │ │ └── pages │ │ │ ├── About.jsx │ │ │ └── Home.jsx │ │ └── vite.config.js ├── 05 Stubs, Spies │ ├── 01 Starting Project │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── location.cy.js │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ └── styles.css │ ├── 02 Creating a Stub │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── location.cy.js │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ └── styles.css │ ├── 03 Fake Stub Implementation │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── location.cy.js │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ └── styles.css │ ├── 04 Evaluating Stub Arguments │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── location.cy.js │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ └── styles.css │ ├── 05 Fixtures │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── location.cy.js │ │ │ ├── fixtures │ │ │ │ └── user-location.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ └── styles.css │ ├── 06 Creating Spies │ │ ├── cypress.config.js │ │ ├── cypress │ │ │ ├── e2e │ │ │ │ └── location.cy.js │ │ │ ├── fixtures │ │ │ │ └── user-location.json │ │ │ └── support │ │ │ │ ├── commands.js │ │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ └── styles.css │ └── 07 Clock │ │ ├── cypress.config.js │ │ ├── cypress │ │ ├── e2e │ │ │ └── location.cy.js │ │ ├── fixtures │ │ │ └── user-location.json │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ ├── public │ │ └── vite.svg │ │ └── styles.css └── 06 Network, Db, Auth │ ├── 01 Starting Project │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ ├── 02 Test Database │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ ├── 03 More Intercepting │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ ├── newsletter.cy.js │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ ├── 04 Testing APIs │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ ├── newsletter.cy.js │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ ├── 05 Testing Auth │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ ├── auth.cy.js │ │ │ ├── newsletter.cy.js │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ ├── 06 Login test │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ ├── auth.cy.js │ │ │ ├── newsletter.cy.js │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ ├── 07 Reusable Login Command │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ │ ├── components │ │ │ ├── Auth.jsx │ │ │ ├── Layout.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NewsletterSignup.jsx │ │ │ └── Takeaways.jsx │ │ ├── data │ │ │ ├── auth.server.js │ │ │ ├── newsletter.server.js │ │ │ └── prisma.server.js │ │ ├── entry.client.jsx │ │ ├── entry.server.jsx │ │ ├── root.jsx │ │ ├── routes │ │ │ ├── index.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.js │ │ │ ├── newsletter.js │ │ │ ├── signup.jsx │ │ │ ├── takeaways.jsx │ │ │ └── takeaways │ │ │ │ └── new.jsx │ │ ├── styles │ │ │ ├── main.css │ │ │ └── tailwind.css │ │ └── util │ │ │ ├── errors.js │ │ │ ├── validation.server.js │ │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ │ ├── e2e │ │ │ ├── auth.cy.js │ │ │ ├── newsletter.cy.js │ │ │ └── takeaways.cy.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── e2e.js │ ├── package.json │ ├── prisma │ │ ├── schema.prisma │ │ ├── seed-test.js │ │ └── seed.js │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json │ └── 08 Finished │ ├── .env │ ├── .env.test │ ├── .eslintrc.js │ ├── app │ ├── components │ │ ├── Auth.jsx │ │ ├── Layout.jsx │ │ ├── Modal.jsx │ │ ├── NewsletterSignup.jsx │ │ └── Takeaways.jsx │ ├── data │ │ ├── auth.server.js │ │ ├── newsletter.server.js │ │ └── prisma.server.js │ ├── entry.client.jsx │ ├── entry.server.jsx │ ├── root.jsx │ ├── routes │ │ ├── index.jsx │ │ ├── login.jsx │ │ ├── logout.js │ │ ├── newsletter.js │ │ ├── signup.jsx │ │ ├── takeaways.jsx │ │ └── takeaways │ │ │ └── new.jsx │ ├── styles │ │ ├── main.css │ │ └── tailwind.css │ └── util │ │ ├── errors.js │ │ ├── validation.server.js │ │ └── wait.js │ ├── cypress.config.js │ ├── cypress │ ├── e2e │ │ ├── auth.cy.js │ │ ├── newsletter.cy.js │ │ └── takeaways.cy.js │ └── support │ │ ├── commands.js │ │ └── e2e.js │ ├── package.json │ ├── prisma │ ├── schema.prisma │ ├── seed-test.js │ └── seed.js │ ├── public │ └── favicon.ico │ ├── remix.config.js │ ├── styles │ └── tailwind.css │ ├── tailwind.config.js │ └── tsconfig.json └── slides └── cypress-e2e-course-slides.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /attachments/01-getting-started-starting-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/attachments/01-getting-started-starting-project.zip -------------------------------------------------------------------------------- /attachments/02-basics-starting-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/attachments/02-basics-starting-project.zip -------------------------------------------------------------------------------- /attachments/03-diving-deeper-starting-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/attachments/03-diving-deeper-starting-project.zip -------------------------------------------------------------------------------- /attachments/04-config-starting-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/attachments/04-config-starting-project.zip -------------------------------------------------------------------------------- /attachments/05-stubs-starting-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/attachments/05-stubs-starting-project.zip -------------------------------------------------------------------------------- /attachments/06-network-auth-starting-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/attachments/06-network-auth-starting-project.zip -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-icons": "^4.7.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/src/App.jsx: -------------------------------------------------------------------------------- 1 | import CourseGoals from './components/CourseGoals'; 2 | import Header from './components/Header'; 3 | 4 | function App() { 5 | return ( 6 | <> 7 |
8 |
9 | 10 |
11 | 12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/src/components/CourseGoal.jsx: -------------------------------------------------------------------------------- 1 | import classes from './CourseGoal.module.css'; 2 | 3 | function CourseGoal({ icon, text }) { 4 | return ( 5 |
  • 6 | {icon} 7 | {text} 8 |
  • 9 | ); 10 | } 11 | 12 | export default CourseGoal; 13 | -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/src/components/CourseGoals.module.css: -------------------------------------------------------------------------------- 1 | .goals { 2 | max-width: 60rem; 3 | margin: 3rem auto; 4 | display: grid; 5 | gap: 1rem; 6 | grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); 7 | } -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | function Header() { 2 | return ( 3 |
    4 | 5 |

    Getting Started with Cypress

    6 |
    7 | ); 8 | } 9 | 10 | export default Header; 11 | -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/01 Getting Started/01 Starting Project/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/cypress/e2e/first-test.cy.js: -------------------------------------------------------------------------------- 1 | describe('template spec', () => { 2 | it('passes', () => { 3 | cy.visit('http://localhost:5173/'); 4 | cy.get('li').should('have.length', 6); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/src/App.jsx: -------------------------------------------------------------------------------- 1 | import CourseGoals from './components/CourseGoals'; 2 | import Header from './components/Header'; 3 | 4 | function App() { 5 | return ( 6 | <> 7 |
    8 |
    9 | 10 |
    11 | 12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/src/components/CourseGoal.jsx: -------------------------------------------------------------------------------- 1 | import classes from './CourseGoal.module.css'; 2 | 3 | function CourseGoal({ icon, text }) { 4 | return ( 5 |
  • 6 | {icon} 7 | {text} 8 |
  • 9 | ); 10 | } 11 | 12 | export default CourseGoal; 13 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/src/components/CourseGoals.module.css: -------------------------------------------------------------------------------- 1 | .goals { 2 | max-width: 60rem; 3 | margin: 3rem auto; 4 | display: grid; 5 | gap: 1rem; 6 | grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); 7 | } -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | function Header() { 2 | return ( 3 |
    4 | 5 |

    Getting Started with Cypress

    6 |
    7 | ); 8 | } 9 | 10 | export default Header; 11 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/01 Getting Started/02 Finished First Test/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react": "^3.1.0", 19 | "vite": "^4.1.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/01 Starting Project/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    React Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 | 15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/01 Starting Project/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/cypress/e2e/basics.cy.js: -------------------------------------------------------------------------------- 1 | describe('template spec', () => { 2 | it('passes', () => { 3 | cy.visit('https://example.cypress.io'); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/02 Added Cypress/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    React Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 | 15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/02 Added Cypress/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/cypress/e2e/basics.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('tasks page', () => { 4 | it('should render the main image', () => { 5 | cy.visit('http://localhost:5173/'); 6 | cy.get('.main-header img'); 7 | }); 8 | 9 | it('should display the page title', () => { 10 | cy.visit('http://localhost:5173/'); 11 | cy.get('h1').contains('My Cypress Course Tasks'); 12 | // cy.contains('My Cypress Course Tasks'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/03 Selecting By Text/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/03 Selecting By Text/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/cypress/e2e/basics.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('tasks page', () => { 4 | it('should render the main image', () => { 5 | cy.visit('http://localhost:5173/'); 6 | cy.get('.main-header img'); 7 | }); 8 | 9 | it('should display the page title', () => { 10 | cy.visit('http://localhost:5173/'); 11 | cy.get('h1').should('have.length', 1); 12 | cy.get('h1').contains('My Cypress Course Tasks'); 13 | // cy.contains('My Cypress Course Tasks'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/04 Implicit vs Explicit Assertions/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/04 Implicit vs Explicit Assertions/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/cypress/e2e/basics.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('tasks page', () => { 4 | it('should render the main image', () => { 5 | cy.visit('http://localhost:5173/'); 6 | cy.get('.main-header').find('img'); 7 | // cy.get('.main-header img'); // => also works! 8 | }); 9 | 10 | it('should display the page title', () => { 11 | cy.visit('http://localhost:5173/'); 12 | cy.get('h1').should('have.length', 1); 13 | cy.get('h1').contains('My Cypress Course Tasks'); 14 | // cy.contains('My Cypress Course Tasks'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/05 get() vs find()/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/05 get() vs find()/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/06 Simulating User Interaction/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/06 Simulating User Interaction/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/07 Simulating Keyboard Typing/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/07 Simulating Keyboard Typing/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/08 Selecting Dropdown Values/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/08 Selecting Dropdown Values/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/cypress/e2e/basics.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('tasks page', () => { 4 | it('should render the main image', () => { 5 | cy.visit('http://localhost:5173/'); 6 | cy.get('.main-header').find('img'); 7 | // cy.get('.main-header img'); // => also works! 8 | }); 9 | 10 | it('should display the page title', () => { 11 | cy.visit('http://localhost:5173/'); 12 | cy.get('h1').should('have.length', 1); 13 | cy.get('h1').contains('My Cypress Course Tasks'); 14 | // cy.contains('My Cypress Course Tasks'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-basics", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react": "^3.1.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/02 Basics/09 Time For More Queries/src/assets/logo.png -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | function Filter({ onFilterChange }) { 2 | function filterChangeHandler(event) { 3 | onFilterChange(event.target.value); 4 | } 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | 17 | export default Filter; 18 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | margin: 3rem auto; 3 | text-align: center; 4 | color: var(--color-gray-400); 5 | } 6 | 7 | .main-header img { 8 | width: 7rem; 9 | height: 7rem; 10 | object-fit: contain; 11 | transform: rotateZ(10deg); 12 | } -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import './Header.css'; 2 | import logo from '../assets/logo.png'; 3 | 4 | function Header() { 5 | return ( 6 |
    7 | A list 8 |

    My Cypress Course Tasks

    9 |
    10 | ); 11 | } 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Modal.css: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 1; 9 | } 10 | 11 | .modal { 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | margin: 0; 16 | padding: 2rem; 17 | transform: translate(-50%, -50%); 18 | width: 40rem; 19 | background-color: var(--color-gray-800); 20 | border: none; 21 | border-radius: 4px; 22 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 23 | z-index: 10; 24 | } 25 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import './Modal.css'; 2 | 3 | function Modal({ children, onClose }) { 4 | return ( 5 | <> 6 |
    7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Modal; 15 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Task.css: -------------------------------------------------------------------------------- 1 | .task { 2 | display: flex; 3 | gap: 1rem; 4 | margin: 1rem 0; 5 | padding: 1rem; 6 | border: 1px solid var(--color-gray-600); 7 | background-color: var(--color-gray-700); 8 | border-radius: 4px; 9 | } 10 | 11 | .task-category { 12 | font-size: 1.25rem; 13 | } 14 | 15 | .task h2 { 16 | margin: 0; 17 | color: var(--color-gray-300); 18 | font-size: 1rem; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .task p { 24 | margin: 0; 25 | color: var(--color-gray-200); 26 | } -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import './Task.css'; 2 | 3 | const CATEGORY_ICONS = { 4 | urgent: '🚨', 5 | important: '🔴', 6 | moderate: '🔵', 7 | low: '🟢', 8 | }; 9 | 10 | function Task({ category, title, summary }) { 11 | return ( 12 |
  • 13 | {CATEGORY_ICONS[category]} 14 |
    15 |

    {title}

    16 |

    {summary}

    17 |
    18 |
  • 19 | ); 20 | } 21 | 22 | export default Task; 23 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/TaskControl.jsx: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | 3 | import './TaskControl.css'; 4 | 5 | function TaskControl({ onStartAddTask, onSetFilter }) { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | ); 12 | } 13 | 14 | export default TaskControl; 15 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/TaskList.css: -------------------------------------------------------------------------------- 1 | .task-list { 2 | list-style: none; 3 | margin: 2rem 0; 4 | padding: 0; 5 | } 6 | 7 | .no-tasks { 8 | text-align: center; 9 | font-weight: bold; 10 | color: var(--color-gray-400); 11 | font-size: 2rem; 12 | margin: 3rem auto; 13 | } 14 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/components/TaskList.jsx: -------------------------------------------------------------------------------- 1 | import Task from './Task'; 2 | import './TaskList.css'; 3 | 4 | function TaskList({ tasks }) { 5 | if (!tasks || tasks.length === 0) { 6 | return

    No tasks found!

    ; 7 | } 8 | 9 | return ( 10 |
      11 | {tasks.map((task) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | } 17 | 18 | export default TaskList; 19 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /code/02 Basics/09 Time For More Queries/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/01 Starting Project/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/02 More on Selecting Elements/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/03 More Assertions/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/04 Working with Values & Aliases/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/05 Getting More Direct Element Access/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/06 Simulating Special Key Presses/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/07 Changing Subjects/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/08 Screenshots/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/03 Diving Deeper/09 should instead of then/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/03 Diving Deeper/09 should instead of then/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/cypress/videos/contact.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/03 Diving Deeper/09 should instead of then/cypress/videos/contact.cy.js.mp4 -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/cypress/videos/navigation.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/03 Diving Deeper/09 should instead of then/cypress/videos/navigation.cy.js.mp4 -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/03 Diving Deeper/09 should instead of then/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/01 Starting Project/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/01 Starting Project/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/cypress/videos/contact.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/01 Starting Project/cypress/videos/contact.cy.js.mp4 -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/cypress/videos/navigation.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/01 Starting Project/cypress/videos/navigation.cy.js.mp4 -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/04 Configuration/01 Starting Project/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/02 Hooks/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/02 Hooks/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/cypress/videos/contact.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/02 Hooks/cypress/videos/contact.cy.js.mp4 -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/cypress/videos/navigation.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/02 Hooks/cypress/videos/navigation.cy.js.mp4 -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-adv", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.8.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.17", 19 | "@types/react-dom": "^18.0.6", 20 | "@vitejs/plugin-react": "^2.1.0", 21 | "vite": "^3.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/04 Configuration/02 Hooks/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | on('task', { 9 | seedDatabase(filename) { 10 | // Run your NodeJS code 11 | // e.g., edit a file here 12 | return filename; 13 | } 14 | }); 15 | }, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/03 Finished/cypress/screenshots/contact.cy.js/contact form -- should submit the form (1).png -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/03 Finished/cypress/screenshots/contact.cy.js/contact form -- should submit the form.png -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/cypress/videos/contact.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/03 Finished/cypress/videos/contact.cy.js.mp4 -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/cypress/videos/navigation.cy.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/04 Configuration/03 Finished/cypress/videos/navigation.cy.js.mp4 -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-adv", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "cypress": "^12.5.1", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.8.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.17", 19 | "@types/react-dom": "^18.0.6", 20 | "@vitejs/plugin-react": "^2.1.0", 21 | "vite": "^3.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from 'react-router-dom'; 2 | 3 | import HomePage from './pages/Home'; 4 | import AboutPage from './pages/About'; 5 | import Header from './components/Header'; 6 | 7 | function App() { 8 | return ( 9 | <> 10 |
    11 |
    12 | 13 | } /> 14 | } /> 15 | 16 |
    17 | 18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | max-width: 60rem; 3 | margin: 2rem auto; 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .header ul { 10 | display: flex; 11 | gap: 1rem; 12 | } 13 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 | <> 4 |
    5 |

    Home Page

    6 |
    7 | 8 | ); 9 | } 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /code/04 Configuration/03 Finished/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/01 Starting Project/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/01 Starting Project/cypress/e2e/location.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('share location', () => { 4 | it('should fetch the user location', () => { 5 | cy.visit('/'); 6 | cy.get('[data-cy="get-loc-btn"]').click(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/01 Starting Project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/02 Creating a Stub/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/02 Creating a Stub/cypress/e2e/location.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('share location', () => { 4 | it('should fetch the user location', () => { 5 | cy.visit('/').then((win) => { 6 | cy.stub(win.navigator.geolocation, 'getCurrentPosition').as('getUserPosition'); 7 | }); 8 | cy.get('[data-cy="get-loc-btn"]').click(); 9 | cy.get('@getUserPosition').should('have.been.called'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/02 Creating a Stub/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/03 Fake Stub Implementation/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/03 Fake Stub Implementation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/04 Evaluating Stub Arguments/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/04 Evaluating Stub Arguments/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/05 Fixtures/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/05 Fixtures/cypress/fixtures/user-location.json: -------------------------------------------------------------------------------- 1 | { 2 | "coords": { 3 | "latitude": 37.5, 4 | "longitude": 48.01 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/05 Fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/06 Creating Spies/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/06 Creating Spies/cypress/fixtures/user-location.json: -------------------------------------------------------------------------------- 1 | { 2 | "coords": { 3 | "latitude": 37.5, 4 | "longitude": 48.01 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/06 Creating Spies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/07 Clock/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:5173', 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/07 Clock/cypress/fixtures/user-location.json: -------------------------------------------------------------------------------- 1 | { 2 | "coords": { 3 | "latitude": 37.5, 4 | "longitude": 48.01 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /code/05 Stubs, Spies/07 Clock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-stubs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.1.0" 13 | }, 14 | "dependencies": { 15 | "cypress": "^12.5.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | it('should display a list of fetched takeaways', () => { 5 | cy.visit('/') 6 | }); 7 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/01 Starting Project/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/01 Starting Project/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | beforeEach(() => { 5 | cy.task('seedDatabase'); 6 | }); 7 | it('should display a list of fetched takeaways', () => { 8 | cy.visit('/'); 9 | cy.get('[data-cy="takeaway-item"]').should('have.length', 2); 10 | }); 11 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/02 Test Database/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/02 Test Database/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | beforeEach(() => { 5 | cy.task('seedDatabase'); 6 | }); 7 | it('should display a list of fetched takeaways', () => { 8 | cy.visit('/'); 9 | cy.get('[data-cy="takeaway-item"]').should('have.length', 2); 10 | }); 11 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/03 More Intercepting/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/03 More Intercepting/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | beforeEach(() => { 5 | cy.task('seedDatabase'); 6 | }); 7 | it('should display a list of fetched takeaways', () => { 8 | cy.visit('/'); 9 | cy.get('[data-cy="takeaway-item"]').should('have.length', 2); 10 | }); 11 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/04 Testing APIs/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/04 Testing APIs/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | beforeEach(() => { 5 | cy.task('seedDatabase'); 6 | }); 7 | it('should display a list of fetched takeaways', () => { 8 | cy.visit('/'); 9 | cy.get('[data-cy="takeaway-item"]').should('have.length', 2); 10 | }); 11 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/05 Testing Auth/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/05 Testing Auth/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | beforeEach(() => { 5 | cy.task('seedDatabase'); 6 | }); 7 | it('should display a list of fetched takeaways', () => { 8 | cy.visit('/'); 9 | cy.get('[data-cy="takeaway-item"]').should('have.length', 2); 10 | }); 11 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/06 Login test/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/06 Login test/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/cypress/e2e/takeaways.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Takeaways', () => { 4 | beforeEach(() => { 5 | cy.task('seedDatabase'); 6 | }); 7 | it('should display a list of fetched takeaways', () => { 8 | cy.visit('/'); 9 | cy.get('[data-cy="takeaway-item"]').should('have.length', 2); 10 | }); 11 | }); -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/07 Reusable Login Command/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/07 Reusable Login Command/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./demo.db" 8 | SESSION_SECRET="supersecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/.env.test: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./test.db" 2 | SESSION_SECRET="testsecure" -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "plugin:cypress/recommended"], 4 | }; 5 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ onClose, children }) { 2 | return ( 3 | <> 4 |
    8 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Modal; 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/components/Takeaways.jsx: -------------------------------------------------------------------------------- 1 | function Takeaways({ items }) { 2 | return ( 3 |
      4 | {items.map((item) => ( 5 |
    • 6 |
      7 |

      {item.title}

      8 |

      {item.body}

      9 |
      10 |
    • 11 | ))} 12 |
    13 | ); 14 | } 15 | 16 | export default Takeaways; 17 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/data/prisma.server.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | /** 4 | * @type PrismaClient 5 | */ 6 | let prisma; 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | prisma = new PrismaClient(); 10 | prisma.$connect(); 11 | } else { 12 | if (!global.__db) { 13 | global.__db = new PrismaClient(); 14 | global.__db.$connect(); 15 | } 16 | prisma = global.__db; 17 | } 18 | 19 | export { prisma }; 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/routes/logout.js: -------------------------------------------------------------------------------- 1 | import { destroyUserSession } from '~/data/auth.server'; 2 | import { BadRequestErrorResponse } from '../util/errors'; 3 | 4 | export function action({ request }) { 5 | if (request.method !== 'POST') { 6 | throw new BadRequestErrorResponse('HTTP method not allowed.'); 7 | } 8 | 9 | return destroyUserSession(request); 10 | } 11 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | width: 1rem; 3 | height: 1rem; 4 | border: 2px solid #fff; 5 | border-bottom-color: transparent; 6 | border-radius: 50%; 7 | display: inline-block; 8 | box-sizing: border-box; 9 | animation: rotation 1s linear infinite; 10 | } 11 | 12 | @keyframes rotation { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/util/errors.js: -------------------------------------------------------------------------------- 1 | export class BadRequestErrorResponse extends Response { 2 | constructor(message, statusText = 'Bad request') { 3 | super(JSON.stringify({ status: 400, message }), { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | }, 7 | status: 400, 8 | statusText: statusText, 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/util/validation.server.js: -------------------------------------------------------------------------------- 1 | export function isValidEmail(email) { 2 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 3 | } 4 | 5 | export function isValidPassword(password) { 6 | return password.length >= 6; 7 | } -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/app/util/wait.js: -------------------------------------------------------------------------------- 1 | export function wait(time) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, time); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | import { seed } from './prisma/seed-test'; 4 | 5 | export default defineConfig({ 6 | e2e: { 7 | baseUrl: 'http://localhost:3000', 8 | setupNodeEvents(on, config) { 9 | // implement node event listeners here 10 | on('task', { 11 | async seedDatabase() { 12 | await seed(); 13 | return null; 14 | } 15 | }) 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/code/06 Network, Db, Auth/08 Finished/public/favicon.ico -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ["**/.*"], 4 | serverPlatform: "node", 5 | future: { 6 | v2_errorBoundary: false, 7 | v2_meta: false, 8 | v2_normalizeFormMethod: false, 9 | v2_routeConvention: false, 10 | unstable_tailwind: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /code/06 Network, Db, Auth/08 Finished/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./app/**/*.{js,ts,jsx,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /slides/cypress-e2e-course-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academind/cypress-e2e-testing-course-resources/ce48722960ff05a9006bd7274336fac40c9884e7/slides/cypress-e2e-course-slides.pdf --------------------------------------------------------------------------------