├── .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 |
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 |
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 |
11 | {tasks.map((task) => (
12 |
13 | ))}
14 |
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 |
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 |
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 |
11 | {tasks.map((task) => (
12 |
13 | ))}
14 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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
--------------------------------------------------------------------------------