├── LICENSE
├── README.md
├── base-concert-venue
├── .env.development.local_template
├── .env.local_template
├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode
│ └── settings.json
├── README.md
├── __tests__
│ ├── __mocks__
│ │ └── fakeData
│ │ │ ├── index.ts
│ │ │ ├── json
│ │ │ ├── bands.json
│ │ │ ├── reservations.json
│ │ │ ├── shows.json
│ │ │ └── users.json
│ │ │ ├── newBand.ts
│ │ │ ├── newReservation.ts
│ │ │ ├── newShow.ts
│ │ │ └── userReservations.ts
│ ├── __snapshots__
│ │ └── snapshot.tsx.snap
│ ├── index.test.tsx
│ └── snapshot.tsx
├── components
│ ├── _common
│ │ ├── Layout.tsx
│ │ ├── LoadingSpinner.tsx
│ │ └── QueryError.tsx
│ ├── auth
│ │ ├── Auth.tsx
│ │ └── SignInError.tsx
│ ├── bands
│ │ └── BandLinkHeading.tsx
│ ├── home
│ │ └── SplashImage.tsx
│ ├── nav
│ │ ├── Nav.tsx
│ │ ├── NavLink.tsx
│ │ └── SignOutButton.tsx
│ ├── reservations
│ │ └── Reservation.tsx
│ └── user
│ │ ├── UserReservation.tsx
│ │ └── UserReservations.tsx
├── db
│ ├── bands.json
│ ├── reservations.json
│ ├── shows.json
│ └── users.json
├── jest.config.js
├── jest.setup.js
├── lib
│ ├── api
│ │ ├── handler.ts
│ │ └── utils.ts
│ ├── auth
│ │ └── utils.ts
│ ├── axios
│ │ ├── axiosInstance.ts
│ │ └── routes.ts
│ ├── db
│ │ ├── constants.ts
│ │ ├── data
│ │ │ ├── bandData.ts
│ │ │ └── generateData.ts
│ │ └── db-utils.ts
│ ├── features
│ │ ├── bands
│ │ │ ├── queries.ts
│ │ │ └── types.ts
│ │ ├── reservations
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── shows
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ └── users
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ ├── useSessionStatus.ts
│ │ │ └── utils.ts
│ └── theme
│ │ └── index.ts
├── next-env.d.ts
├── package-lock.json
├── package.json
├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── api
│ │ ├── auth
│ │ │ └── [...nextauth].ts
│ │ ├── bands
│ │ │ └── index.ts
│ │ ├── reservations
│ │ │ └── [reservationId].ts
│ │ ├── shows
│ │ │ ├── [showId].ts
│ │ │ └── index.ts
│ │ └── users
│ │ │ ├── [userId]
│ │ │ └── reservations.ts
│ │ │ └── index.ts
│ ├── auth
│ │ └── signin.tsx
│ ├── bands
│ │ ├── [bandId].tsx
│ │ └── index.tsx
│ ├── confirmation
│ │ └── [reservationId].tsx
│ ├── index.tsx
│ ├── reservations
│ │ └── [showId].tsx
│ ├── shows
│ │ └── index.tsx
│ └── user
│ │ └── index.tsx
├── public
│ ├── band-images
│ │ ├── band1.jpg
│ │ ├── band10.jpg
│ │ ├── band11.jpg
│ │ ├── band12.jpg
│ │ ├── band13.jpg
│ │ ├── band14.jpg
│ │ ├── band15.jpg
│ │ ├── band16.jpg
│ │ ├── band17.jpg
│ │ ├── band18.jpg
│ │ ├── band2.jpg
│ │ ├── band3.jpg
│ │ ├── band4.jpg
│ │ ├── band5.jpg
│ │ ├── band6.jpg
│ │ ├── band7.jpg
│ │ ├── band8.jpg
│ │ └── band9.jpg
│ ├── favicon.ico
│ └── splash
│ │ ├── heart-hands-logo.jpeg
│ │ └── heart-hands.jpg
├── scripts
│ └── reset-db.sh
├── tsconfig.json
└── types
│ ├── next-auth.types.d.ts
│ └── types.d.ts
├── nextjs-msw2-example
├── .gitignore
├── README.md
├── __mocks__
│ ├── handlers.ts
│ └── server.ts
├── jest.config.js
├── jest.polyfills.js
├── jest.setup.js
├── package-lock.json
├── package.json
├── pages
│ ├── _app.tsx
│ ├── api
│ │ └── description.ts
│ ├── home
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ └── index.module.css
├── public
│ ├── favicon.ico
│ ├── mockServiceWorker.js
│ └── vercel.svg
├── styles
│ └── global.css
├── tsconfig.json
└── types.d.ts
├── section-start-code
├── section-05-testdb
│ ├── .env.development.local_template
│ ├── .env.local_template
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmrc
│ ├── .nvmrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── __tests__
│ │ ├── __mocks__
│ │ │ ├── fakeData
│ │ │ │ ├── index.ts
│ │ │ │ ├── json
│ │ │ │ │ ├── bands.json
│ │ │ │ │ ├── reservations.json
│ │ │ │ │ ├── shows.json
│ │ │ │ │ └── users.json
│ │ │ │ ├── newBand.ts
│ │ │ │ ├── newReservation.ts
│ │ │ │ ├── newShow.ts
│ │ │ │ └── userReservations.ts
│ │ │ └── msw
│ │ │ │ ├── handlers.js
│ │ │ │ └── server.js
│ │ └── ui
│ │ │ ├── band.test.tsx
│ │ │ ├── home.test.tsx
│ │ │ ├── reservation.test.tsx
│ │ │ └── user-reservations.test.tsx
│ ├── components
│ │ ├── _common
│ │ │ ├── Layout.tsx
│ │ │ ├── LoadingSpinner.tsx
│ │ │ └── QueryError.tsx
│ │ ├── auth
│ │ │ ├── Auth.tsx
│ │ │ └── SignInError.tsx
│ │ ├── bands
│ │ │ └── BandLinkHeading.tsx
│ │ ├── home
│ │ │ └── SplashImage.tsx
│ │ ├── nav
│ │ │ ├── Nav.tsx
│ │ │ ├── NavLink.tsx
│ │ │ └── SignOutButton.tsx
│ │ ├── reservations
│ │ │ └── Reservation.tsx
│ │ └── user
│ │ │ ├── UserReservation.tsx
│ │ │ └── UserReservations.tsx
│ ├── db
│ │ ├── bands.json
│ │ ├── reservations.json
│ │ ├── shows.json
│ │ └── users.json
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── lib
│ │ ├── api
│ │ │ ├── handler.ts
│ │ │ └── utils.ts
│ │ ├── auth
│ │ │ └── utils.ts
│ │ ├── axios
│ │ │ ├── axiosInstance.ts
│ │ │ └── routes.ts
│ │ ├── db
│ │ │ ├── constants.ts
│ │ │ ├── data
│ │ │ │ ├── bandData.ts
│ │ │ │ └── generateData.ts
│ │ │ └── db-utils.ts
│ │ ├── features
│ │ │ ├── bands
│ │ │ │ ├── queries.ts
│ │ │ │ └── types.ts
│ │ │ ├── reservations
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── shows
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ └── users
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── useSessionStatus.ts
│ │ │ │ └── utils.ts
│ │ └── theme
│ │ │ └── index.ts
│ ├── next-env.d.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api
│ │ │ ├── auth
│ │ │ │ └── [...nextauth].ts
│ │ │ ├── bands
│ │ │ │ └── index.ts
│ │ │ ├── reservations
│ │ │ │ └── [reservationId].ts
│ │ │ ├── shows
│ │ │ │ ├── [showId].ts
│ │ │ │ └── index.ts
│ │ │ └── users
│ │ │ │ ├── [userId]
│ │ │ │ └── reservations.ts
│ │ │ │ └── index.ts
│ │ ├── auth
│ │ │ └── signin.tsx
│ │ ├── bands
│ │ │ ├── [bandId].tsx
│ │ │ └── index.tsx
│ │ ├── confirmation
│ │ │ └── [reservationId].tsx
│ │ ├── index.tsx
│ │ ├── reservations
│ │ │ └── [showId].tsx
│ │ ├── shows
│ │ │ └── index.tsx
│ │ └── user
│ │ │ └── index.tsx
│ ├── public
│ │ ├── band-images
│ │ │ ├── band1.jpg
│ │ │ ├── band10.jpg
│ │ │ ├── band11.jpg
│ │ │ ├── band12.jpg
│ │ │ ├── band13.jpg
│ │ │ ├── band14.jpg
│ │ │ ├── band15.jpg
│ │ │ ├── band16.jpg
│ │ │ ├── band17.jpg
│ │ │ ├── band18.jpg
│ │ │ ├── band2.jpg
│ │ │ ├── band3.jpg
│ │ │ ├── band4.jpg
│ │ │ ├── band5.jpg
│ │ │ ├── band6.jpg
│ │ │ ├── band7.jpg
│ │ │ ├── band8.jpg
│ │ │ └── band9.jpg
│ │ ├── favicon.ico
│ │ └── splash
│ │ │ ├── heart-hands-logo.jpeg
│ │ │ └── heart-hands.jpg
│ ├── scripts
│ │ └── reset-db.sh
│ ├── tsconfig.json
│ └── types
│ │ ├── next-auth.types.d.ts
│ │ └── types.d.ts
├── section-06-routes-and-cypress
│ ├── .env.development.local_template
│ ├── .env.local_template
│ ├── .env.test.local_template
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmrc
│ ├── .nvmrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── __tests__
│ │ ├── __mocks__
│ │ │ ├── db
│ │ │ │ ├── bands.json
│ │ │ │ ├── reservations.json
│ │ │ │ ├── shows.json
│ │ │ │ ├── user.json
│ │ │ │ └── utils
│ │ │ │ │ └── reset-db.ts
│ │ │ ├── fakeData
│ │ │ │ ├── index.ts
│ │ │ │ ├── json
│ │ │ │ │ ├── bands.json
│ │ │ │ │ ├── reservations.json
│ │ │ │ │ ├── shows.json
│ │ │ │ │ └── users.json
│ │ │ │ ├── newBand.ts
│ │ │ │ ├── newReservation.ts
│ │ │ │ ├── newShow.ts
│ │ │ │ └── userReservations.ts
│ │ │ └── msw
│ │ │ │ ├── handlers.js
│ │ │ │ └── server.js
│ │ └── ui
│ │ │ ├── band.test.tsx
│ │ │ ├── home.test.tsx
│ │ │ ├── reservation.test.tsx
│ │ │ └── user-reservations.test.tsx
│ ├── components
│ │ ├── _common
│ │ │ ├── Layout.tsx
│ │ │ ├── LoadingSpinner.tsx
│ │ │ └── QueryError.tsx
│ │ ├── auth
│ │ │ ├── Auth.tsx
│ │ │ └── SignInError.tsx
│ │ ├── bands
│ │ │ └── BandLinkHeading.tsx
│ │ ├── home
│ │ │ └── SplashImage.tsx
│ │ ├── nav
│ │ │ ├── Nav.tsx
│ │ │ ├── NavLink.tsx
│ │ │ └── SignOutButton.tsx
│ │ ├── reservations
│ │ │ └── Reservation.tsx
│ │ └── user
│ │ │ ├── UserReservation.tsx
│ │ │ └── UserReservations.tsx
│ ├── db
│ │ ├── bands.json
│ │ ├── reservations.json
│ │ ├── shows.json
│ │ └── users.json
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── lib
│ │ ├── api
│ │ │ ├── handler.ts
│ │ │ └── utils.ts
│ │ ├── auth
│ │ │ └── utils.ts
│ │ ├── axios
│ │ │ ├── axiosInstance.ts
│ │ │ └── routes.ts
│ │ ├── db
│ │ │ ├── constants.ts
│ │ │ ├── data
│ │ │ │ ├── bandData.ts
│ │ │ │ └── generateData.ts
│ │ │ └── db-utils.ts
│ │ ├── features
│ │ │ ├── bands
│ │ │ │ ├── queries.ts
│ │ │ │ └── types.ts
│ │ │ ├── reservations
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── shows
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ └── users
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── useSessionStatus.ts
│ │ │ │ └── utils.ts
│ │ └── theme
│ │ │ └── index.ts
│ ├── next-env.d.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api
│ │ │ ├── auth
│ │ │ │ └── [...nextauth].ts
│ │ │ ├── bands
│ │ │ │ └── index.ts
│ │ │ ├── reservations
│ │ │ │ └── [reservationId].ts
│ │ │ ├── shows
│ │ │ │ ├── [showId].ts
│ │ │ │ └── index.ts
│ │ │ └── users
│ │ │ │ ├── [userId]
│ │ │ │ └── reservations.ts
│ │ │ │ └── index.ts
│ │ ├── auth
│ │ │ └── signin.tsx
│ │ ├── bands
│ │ │ ├── [bandId].tsx
│ │ │ └── index.tsx
│ │ ├── confirmation
│ │ │ └── [reservationId].tsx
│ │ ├── index.tsx
│ │ ├── reservations
│ │ │ └── [showId].tsx
│ │ ├── shows
│ │ │ └── index.tsx
│ │ └── user
│ │ │ └── index.tsx
│ ├── public
│ │ ├── band-images
│ │ │ ├── band1.jpg
│ │ │ ├── band10.jpg
│ │ │ ├── band11.jpg
│ │ │ ├── band12.jpg
│ │ │ ├── band13.jpg
│ │ │ ├── band14.jpg
│ │ │ ├── band15.jpg
│ │ │ ├── band16.jpg
│ │ │ ├── band17.jpg
│ │ │ ├── band18.jpg
│ │ │ ├── band2.jpg
│ │ │ ├── band3.jpg
│ │ │ ├── band4.jpg
│ │ │ ├── band5.jpg
│ │ │ ├── band6.jpg
│ │ │ ├── band7.jpg
│ │ │ ├── band8.jpg
│ │ │ └── band9.jpg
│ │ ├── favicon.ico
│ │ └── splash
│ │ │ ├── heart-hands-logo.jpeg
│ │ │ └── heart-hands.jpg
│ ├── scripts
│ │ └── reset-db.sh
│ ├── tsconfig.json
│ └── types
│ │ ├── next-auth.types.d.ts
│ │ └── types.d.ts
├── section-07-isr-and-data-updates
│ ├── .env.development.local_template
│ ├── .env.local_template
│ ├── .env.test.local_template
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmrc
│ ├── .nvmrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── __tests__
│ │ ├── __mocks__
│ │ │ ├── db
│ │ │ │ ├── bands.json
│ │ │ │ ├── reservations.json
│ │ │ │ ├── shows.json
│ │ │ │ ├── users.json
│ │ │ │ └── utils
│ │ │ │ │ └── reset-db.ts
│ │ │ ├── fakeData
│ │ │ │ ├── index.ts
│ │ │ │ ├── json
│ │ │ │ │ ├── bands.json
│ │ │ │ │ ├── reservations.json
│ │ │ │ │ ├── shows.json
│ │ │ │ │ └── users.json
│ │ │ │ ├── newBand.ts
│ │ │ │ ├── newReservation.ts
│ │ │ │ ├── newShow.ts
│ │ │ │ └── userReservations.ts
│ │ │ └── msw
│ │ │ │ ├── handlers.js
│ │ │ │ └── server.js
│ │ └── ui
│ │ │ ├── band.test.tsx
│ │ │ ├── home.test.tsx
│ │ │ ├── reservation.test.tsx
│ │ │ └── user-reservations.test.tsx
│ ├── components
│ │ ├── _common
│ │ │ ├── Layout.tsx
│ │ │ ├── LoadingSpinner.tsx
│ │ │ └── QueryError.tsx
│ │ ├── auth
│ │ │ ├── Auth.tsx
│ │ │ └── SignInError.tsx
│ │ ├── bands
│ │ │ └── BandLinkHeading.tsx
│ │ ├── home
│ │ │ └── SplashImage.tsx
│ │ ├── nav
│ │ │ ├── Nav.tsx
│ │ │ ├── NavLink.tsx
│ │ │ └── SignOutButton.tsx
│ │ ├── reservations
│ │ │ └── Reservation.tsx
│ │ └── user
│ │ │ ├── UserReservation.tsx
│ │ │ └── UserReservations.tsx
│ ├── cypress.json
│ ├── cypress
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ └── routes.test.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── index.js
│ ├── db
│ │ ├── bands.json
│ │ ├── reservations.json
│ │ ├── shows.json
│ │ └── users.json
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── lib
│ │ ├── api
│ │ │ ├── handler.ts
│ │ │ └── utils.ts
│ │ ├── auth
│ │ │ └── utils.ts
│ │ ├── axios
│ │ │ ├── axiosInstance.ts
│ │ │ └── routes.ts
│ │ ├── db
│ │ │ ├── constants.ts
│ │ │ ├── data
│ │ │ │ ├── bandData.ts
│ │ │ │ └── generateData.ts
│ │ │ └── db-utils.ts
│ │ ├── features
│ │ │ ├── bands
│ │ │ │ ├── queries.ts
│ │ │ │ └── types.ts
│ │ │ ├── reservations
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── shows
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ └── users
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── useSessionStatus.ts
│ │ │ │ └── utils.ts
│ │ └── theme
│ │ │ └── index.ts
│ ├── next-env.d.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api
│ │ │ ├── auth
│ │ │ │ └── [...nextauth].ts
│ │ │ ├── bands
│ │ │ │ └── index.ts
│ │ │ ├── reservations
│ │ │ │ └── [reservationId].ts
│ │ │ ├── shows
│ │ │ │ ├── [showId].ts
│ │ │ │ └── index.ts
│ │ │ └── users
│ │ │ │ ├── [userId]
│ │ │ │ └── reservations.ts
│ │ │ │ └── index.ts
│ │ ├── auth
│ │ │ └── signin.tsx
│ │ ├── bands
│ │ │ ├── [bandId].tsx
│ │ │ └── index.tsx
│ │ ├── confirmation
│ │ │ └── [reservationId].tsx
│ │ ├── index.tsx
│ │ ├── reservations
│ │ │ └── [showId].tsx
│ │ ├── shows
│ │ │ └── index.tsx
│ │ └── user
│ │ │ └── index.tsx
│ ├── public
│ │ ├── band-images
│ │ │ ├── band1.jpg
│ │ │ ├── band10.jpg
│ │ │ ├── band11.jpg
│ │ │ ├── band12.jpg
│ │ │ ├── band13.jpg
│ │ │ ├── band14.jpg
│ │ │ ├── band15.jpg
│ │ │ ├── band16.jpg
│ │ │ ├── band17.jpg
│ │ │ ├── band18.jpg
│ │ │ ├── band2.jpg
│ │ │ ├── band3.jpg
│ │ │ ├── band4.jpg
│ │ │ ├── band5.jpg
│ │ │ ├── band6.jpg
│ │ │ ├── band7.jpg
│ │ │ ├── band8.jpg
│ │ │ └── band9.jpg
│ │ ├── favicon.ico
│ │ └── splash
│ │ │ ├── heart-hands-logo.jpeg
│ │ │ └── heart-hands.jpg
│ ├── scripts
│ │ └── reset-db.sh
│ ├── tsconfig.json
│ └── types
│ │ ├── next-auth.types.d.ts
│ │ └── types.d.ts
├── section-08-auth
│ ├── .env.development.local_template
│ ├── .env.local_template
│ ├── .env.test.local_template
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmrc
│ ├── .nvmrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── __tests__
│ │ ├── __mocks__
│ │ │ ├── db
│ │ │ │ ├── bands.json
│ │ │ │ ├── reservations.json
│ │ │ │ ├── shows.json
│ │ │ │ ├── users.json
│ │ │ │ └── utils
│ │ │ │ │ └── reset-db.ts
│ │ │ ├── fakeData
│ │ │ │ ├── index.ts
│ │ │ │ ├── json
│ │ │ │ │ ├── bands.json
│ │ │ │ │ ├── reservations.json
│ │ │ │ │ ├── shows.json
│ │ │ │ │ └── users.json
│ │ │ │ ├── newBand.ts
│ │ │ │ ├── newReservation.ts
│ │ │ │ ├── newShow.ts
│ │ │ │ └── userReservations.ts
│ │ │ └── msw
│ │ │ │ ├── handlers.js
│ │ │ │ └── server.js
│ │ └── ui
│ │ │ ├── band.test.tsx
│ │ │ ├── home.test.tsx
│ │ │ ├── reservation.test.tsx
│ │ │ └── user-reservations.test.tsx
│ ├── components
│ │ ├── _common
│ │ │ ├── Layout.tsx
│ │ │ ├── LoadingSpinner.tsx
│ │ │ └── QueryError.tsx
│ │ ├── auth
│ │ │ ├── Auth.tsx
│ │ │ └── SignInError.tsx
│ │ ├── bands
│ │ │ └── BandLinkHeading.tsx
│ │ ├── home
│ │ │ └── SplashImage.tsx
│ │ ├── nav
│ │ │ ├── Nav.tsx
│ │ │ ├── NavLink.tsx
│ │ │ └── SignOutButton.tsx
│ │ ├── reservations
│ │ │ └── Reservation.tsx
│ │ └── user
│ │ │ ├── UserReservation.tsx
│ │ │ └── UserReservations.tsx
│ ├── cypress.json
│ ├── cypress
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ ├── bands-isr.test.js
│ │ │ ├── isr-revalidation.test.js
│ │ │ ├── routes.test.js
│ │ │ ├── shows-isr.test.js
│ │ │ └── swr-revalidation.test.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── index.js
│ ├── db
│ │ ├── bands.json
│ │ ├── reservations.json
│ │ ├── shows.json
│ │ └── users.json
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── lib
│ │ ├── api
│ │ │ ├── handler.ts
│ │ │ └── utils.ts
│ │ ├── auth
│ │ │ └── utils.ts
│ │ ├── axios
│ │ │ ├── axiosInstance.ts
│ │ │ └── routes.ts
│ │ ├── db
│ │ │ ├── constants.ts
│ │ │ ├── data
│ │ │ │ ├── bandData.ts
│ │ │ │ └── generateData.ts
│ │ │ └── db-utils.ts
│ │ ├── features
│ │ │ ├── bands
│ │ │ │ ├── queries.ts
│ │ │ │ └── types.ts
│ │ │ ├── reservations
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── shows
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ └── users
│ │ │ │ ├── queries.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── useSessionStatus.ts
│ │ │ │ └── utils.ts
│ │ └── theme
│ │ │ └── index.ts
│ ├── next-env.d.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api
│ │ │ ├── auth
│ │ │ │ └── [...nextauth].ts
│ │ │ ├── bands
│ │ │ │ └── index.ts
│ │ │ ├── reservations
│ │ │ │ └── [reservationId].ts
│ │ │ ├── revalidate
│ │ │ │ └── index.ts
│ │ │ ├── shows
│ │ │ │ ├── [showId].ts
│ │ │ │ └── index.ts
│ │ │ └── users
│ │ │ │ ├── [userId]
│ │ │ │ └── reservations.ts
│ │ │ │ └── index.ts
│ │ ├── auth
│ │ │ └── signin.tsx
│ │ ├── bands
│ │ │ ├── [bandId].tsx
│ │ │ └── index.tsx
│ │ ├── confirmation
│ │ │ └── [reservationId].tsx
│ │ ├── index.tsx
│ │ ├── reservations
│ │ │ └── [showId].tsx
│ │ ├── shows
│ │ │ └── index.tsx
│ │ └── user
│ │ │ └── index.tsx
│ ├── public
│ │ ├── band-images
│ │ │ ├── band1.jpg
│ │ │ ├── band10.jpg
│ │ │ ├── band11.jpg
│ │ │ ├── band12.jpg
│ │ │ ├── band13.jpg
│ │ │ ├── band14.jpg
│ │ │ ├── band15.jpg
│ │ │ ├── band16.jpg
│ │ │ ├── band17.jpg
│ │ │ ├── band18.jpg
│ │ │ ├── band2.jpg
│ │ │ ├── band3.jpg
│ │ │ ├── band4.jpg
│ │ │ ├── band5.jpg
│ │ │ ├── band6.jpg
│ │ │ ├── band7.jpg
│ │ │ ├── band8.jpg
│ │ │ └── band9.jpg
│ │ ├── favicon.ico
│ │ └── splash
│ │ │ ├── heart-hands-logo.jpeg
│ │ │ └── heart-hands.jpg
│ ├── scripts
│ │ └── reset-db.sh
│ ├── tsconfig.json
│ └── types
│ │ ├── next-auth.types.d.ts
│ │ └── types.d.ts
└── section-09-api
│ ├── .env.development.local_template
│ ├── .env.local_template
│ ├── .env.test.local_template
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmrc
│ ├── .nvmrc
│ ├── .vscode
│ └── settings.json
│ ├── README.md
│ ├── __tests__
│ ├── __mocks__
│ │ ├── db
│ │ │ ├── bands.json
│ │ │ ├── reservations.json
│ │ │ ├── shows.json
│ │ │ ├── users.json
│ │ │ └── utils
│ │ │ │ └── reset-db.ts
│ │ ├── fakeData
│ │ │ ├── index.ts
│ │ │ ├── json
│ │ │ │ ├── bands.json
│ │ │ │ ├── reservations.json
│ │ │ │ ├── shows.json
│ │ │ │ └── users.json
│ │ │ ├── newBand.ts
│ │ │ ├── newReservation.ts
│ │ │ ├── newShow.ts
│ │ │ └── userReservations.ts
│ │ └── msw
│ │ │ ├── handlers.js
│ │ │ └── server.js
│ └── ui
│ │ ├── band.test.tsx
│ │ ├── home.test.tsx
│ │ ├── reservation.test.tsx
│ │ └── user-reservations.test.tsx
│ ├── components
│ ├── _common
│ │ ├── Layout.tsx
│ │ ├── LoadingSpinner.tsx
│ │ └── QueryError.tsx
│ ├── auth
│ │ ├── Auth.tsx
│ │ └── SignInError.tsx
│ ├── bands
│ │ └── BandLinkHeading.tsx
│ ├── home
│ │ └── SplashImage.tsx
│ ├── nav
│ │ ├── Nav.tsx
│ │ ├── NavLink.tsx
│ │ └── SignOutButton.tsx
│ ├── reservations
│ │ └── Reservation.tsx
│ └── user
│ │ ├── UserReservation.tsx
│ │ └── UserReservations.tsx
│ ├── cypress.json
│ ├── cypress
│ ├── fixtures
│ │ └── protected-pages.json
│ ├── integration
│ │ ├── auth
│ │ │ └── auth-wrapper.test.js
│ │ ├── isr
│ │ │ ├── bands-isr.test.js
│ │ │ ├── isr-revalidation.test.js
│ │ │ └── shows-isr.test.js
│ │ ├── routes
│ │ │ └── routes.test.js
│ │ ├── swr-revalidation
│ │ │ └── swr-revalidation.test.js
│ │ ├── tickets
│ │ │ └── e2e.test.js
│ │ └── user-page
│ │ │ └── user.test.js
│ ├── plugins
│ │ └── index.js
│ └── support
│ │ ├── commands.js
│ │ └── index.js
│ ├── db
│ ├── bands.json
│ ├── reservations.json
│ ├── shows.json
│ └── users.json
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── lib
│ ├── api
│ │ ├── handler.ts
│ │ └── utils.ts
│ ├── auth
│ │ └── utils.ts
│ ├── axios
│ │ ├── axiosInstance.ts
│ │ └── routes.ts
│ ├── db
│ │ ├── constants.ts
│ │ ├── data
│ │ │ ├── bandData.ts
│ │ │ └── generateData.ts
│ │ └── db-utils.ts
│ ├── features
│ │ ├── bands
│ │ │ ├── queries.ts
│ │ │ └── types.ts
│ │ ├── reservations
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── shows
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ └── users
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ ├── useSessionStatus.ts
│ │ │ └── utils.ts
│ └── theme
│ │ └── index.ts
│ ├── next-env.d.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── api
│ │ ├── auth
│ │ │ └── [...nextauth].ts
│ │ ├── bands
│ │ │ └── index.ts
│ │ ├── reservations
│ │ │ └── [reservationId].ts
│ │ ├── revalidate
│ │ │ └── index.ts
│ │ ├── shows
│ │ │ ├── [showId].ts
│ │ │ └── index.ts
│ │ └── users
│ │ │ ├── [userId]
│ │ │ └── reservations.ts
│ │ │ └── index.ts
│ ├── auth
│ │ └── signin.tsx
│ ├── bands
│ │ ├── [bandId].tsx
│ │ └── index.tsx
│ ├── confirmation
│ │ └── [reservationId].tsx
│ ├── index.tsx
│ ├── reservations
│ │ └── [showId].tsx
│ ├── shows
│ │ └── index.tsx
│ └── user
│ │ └── index.tsx
│ ├── public
│ ├── band-images
│ │ ├── band1.jpg
│ │ ├── band10.jpg
│ │ ├── band11.jpg
│ │ ├── band12.jpg
│ │ ├── band13.jpg
│ │ ├── band14.jpg
│ │ ├── band15.jpg
│ │ ├── band16.jpg
│ │ ├── band17.jpg
│ │ ├── band18.jpg
│ │ ├── band2.jpg
│ │ ├── band3.jpg
│ │ ├── band4.jpg
│ │ ├── band5.jpg
│ │ ├── band6.jpg
│ │ ├── band7.jpg
│ │ ├── band8.jpg
│ │ └── band9.jpg
│ ├── favicon.ico
│ └── splash
│ │ ├── heart-hands-logo.jpeg
│ │ └── heart-hands.jpg
│ ├── scripts
│ └── reset-db.sh
│ ├── tsconfig.json
│ └── types
│ ├── next-auth.types.d.ts
│ └── types.d.ts
├── tested-concert-venue-nextjs12.2-cypress10.3
├── .env.development.local_template
├── .env.local_template
├── .env.test.local_template
├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode
│ └── settings.json
├── README.md
├── __tests__
│ ├── __mocks__
│ │ ├── db
│ │ │ ├── bands.json
│ │ │ ├── reservations.json
│ │ │ ├── shows.json
│ │ │ ├── users.json
│ │ │ └── utils
│ │ │ │ └── reset-db.ts
│ │ ├── fakeData
│ │ │ ├── index.ts
│ │ │ ├── json
│ │ │ │ ├── bands.json
│ │ │ │ ├── reservations.json
│ │ │ │ ├── shows.json
│ │ │ │ └── users.json
│ │ │ ├── newBand.ts
│ │ │ ├── newReservation.ts
│ │ │ ├── newShow.ts
│ │ │ └── userReservations.ts
│ │ └── msw
│ │ │ ├── handlers.js
│ │ │ └── server.js
│ ├── api
│ │ ├── bands.test.ts
│ │ ├── reservations.test.ts
│ │ ├── shows.test.ts
│ │ └── user.test.ts
│ └── ui
│ │ ├── band.test.tsx
│ │ ├── home.test.tsx
│ │ ├── reservation.test.tsx
│ │ └── user-reservations.test.tsx
├── components
│ ├── _common
│ │ ├── Layout.tsx
│ │ ├── LoadingSpinner.tsx
│ │ └── QueryError.tsx
│ ├── auth
│ │ ├── Auth.tsx
│ │ └── SignInError.tsx
│ ├── bands
│ │ └── BandLinkHeading.tsx
│ ├── home
│ │ └── SplashImage.tsx
│ ├── nav
│ │ ├── Nav.tsx
│ │ ├── NavLink.tsx
│ │ └── SignOutButton.tsx
│ ├── reservations
│ │ └── Reservation.tsx
│ └── user
│ │ ├── UserReservation.tsx
│ │ └── UserReservations.tsx
├── cypress.config.ts
├── cypress
│ ├── e2e
│ │ ├── auth
│ │ │ └── auth-wrapper.cy.js
│ │ ├── isr
│ │ │ ├── bands-isr.cy.js
│ │ │ ├── isr-revalidation.cy.js
│ │ │ └── shows-isr.cy.js
│ │ ├── routes
│ │ │ └── routes.cy.js
│ │ ├── swr-revalidation
│ │ │ └── swr-revalidation.cy.js
│ │ ├── tickets
│ │ │ └── e2e.cy.js
│ │ └── user-page
│ │ │ └── user.cy.js
│ ├── fixtures
│ │ └── protected-pages.json
│ └── support
│ │ ├── commands.js
│ │ └── e2e.js
├── db
│ ├── bands.json
│ ├── reservations.json
│ ├── shows.json
│ └── users.json
├── jest.config.js
├── jest.setup.js
├── lib
│ ├── api
│ │ ├── handler.ts
│ │ └── utils.ts
│ ├── auth
│ │ ├── __mocks__
│ │ │ └── utils.ts
│ │ └── utils.ts
│ ├── axios
│ │ ├── axiosInstance.ts
│ │ └── routes.ts
│ ├── db
│ │ ├── constants.ts
│ │ ├── data
│ │ │ ├── bandData.ts
│ │ │ └── generateData.ts
│ │ └── db-utils.ts
│ ├── features
│ │ ├── bands
│ │ │ ├── queries.ts
│ │ │ └── types.ts
│ │ ├── reservations
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── shows
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ └── users
│ │ │ ├── queries.ts
│ │ │ ├── types.ts
│ │ │ ├── useSessionStatus.ts
│ │ │ └── utils.ts
│ └── theme
│ │ └── index.ts
├── next-env.d.ts
├── package-lock.json
├── package.json
├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── api
│ │ ├── auth
│ │ │ └── [...nextauth].ts
│ │ ├── bands
│ │ │ └── index.ts
│ │ ├── reservations
│ │ │ └── [reservationId].ts
│ │ ├── revalidate
│ │ │ └── index.ts
│ │ ├── shows
│ │ │ ├── [showId].ts
│ │ │ └── index.ts
│ │ └── users
│ │ │ ├── [userId]
│ │ │ └── reservations.ts
│ │ │ └── index.ts
│ ├── auth
│ │ └── signin.tsx
│ ├── bands
│ │ ├── [bandId].tsx
│ │ └── index.tsx
│ ├── confirmation
│ │ └── [reservationId].tsx
│ ├── index.tsx
│ ├── reservations
│ │ └── [showId].tsx
│ ├── shows
│ │ └── index.tsx
│ └── user
│ │ └── index.tsx
├── public
│ ├── band-images
│ │ ├── band1.jpg
│ │ ├── band10.jpg
│ │ ├── band11.jpg
│ │ ├── band12.jpg
│ │ ├── band13.jpg
│ │ ├── band14.jpg
│ │ ├── band15.jpg
│ │ ├── band16.jpg
│ │ ├── band17.jpg
│ │ ├── band18.jpg
│ │ ├── band2.jpg
│ │ ├── band3.jpg
│ │ ├── band4.jpg
│ │ ├── band5.jpg
│ │ ├── band6.jpg
│ │ ├── band7.jpg
│ │ ├── band8.jpg
│ │ └── band9.jpg
│ ├── favicon.ico
│ └── splash
│ │ ├── heart-hands-logo.jpeg
│ │ └── heart-hands.jpg
├── scripts
│ └── reset-db.sh
├── tsconfig.json
└── types
│ ├── next-auth.types.d.ts
│ └── types.d.ts
└── tested-concert-venue
├── .env.development.local_template
├── .env.local_template
├── .env.test.local_template
├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode
└── settings.json
├── README.md
├── __tests__
├── __mocks__
│ ├── db
│ │ ├── bands.json
│ │ ├── reservations.json
│ │ ├── shows.json
│ │ ├── users.json
│ │ └── utils
│ │ │ └── reset-db.ts
│ ├── fakeData
│ │ ├── index.ts
│ │ ├── json
│ │ │ ├── bands.json
│ │ │ ├── reservations.json
│ │ │ ├── shows.json
│ │ │ └── users.json
│ │ ├── newBand.ts
│ │ ├── newReservation.ts
│ │ ├── newShow.ts
│ │ └── userReservations.ts
│ └── msw
│ │ ├── handlers.js
│ │ └── server.js
├── api
│ ├── bands.test.ts
│ ├── reservations.test.ts
│ ├── shows.test.ts
│ └── user.test.ts
└── ui
│ ├── band.test.tsx
│ ├── home.test.tsx
│ ├── reservation.test.tsx
│ └── user-reservations.test.tsx
├── components
├── _common
│ ├── Layout.tsx
│ ├── LoadingSpinner.tsx
│ └── QueryError.tsx
├── auth
│ ├── Auth.tsx
│ └── SignInError.tsx
├── bands
│ └── BandLinkHeading.tsx
├── home
│ └── SplashImage.tsx
├── nav
│ ├── Nav.tsx
│ ├── NavLink.tsx
│ └── SignOutButton.tsx
├── reservations
│ └── Reservation.tsx
└── user
│ ├── UserReservation.tsx
│ └── UserReservations.tsx
├── cypress.json
├── cypress
├── fixtures
│ └── protected-pages.json
├── integration
│ ├── auth
│ │ └── auth-wrapper.test.js
│ ├── isr
│ │ ├── bands-isr.test.js
│ │ ├── bands-isr.test.ts
│ │ ├── isr-revalidation.test.js
│ │ └── shows-isr.test.js
│ ├── routes
│ │ └── routes.test.js
│ ├── swr-revalidation
│ │ └── swr-revalidation.test.js
│ ├── tickets
│ │ └── e2e.test.js
│ └── user-page
│ │ └── user.test.js
├── plugins
│ └── index.js
└── support
│ ├── commands.js
│ └── index.js
├── db
├── bands.json
├── reservations.json
├── shows.json
└── users.json
├── jest.config.js
├── jest.setup.js
├── lib
├── api
│ ├── handler.ts
│ └── utils.ts
├── auth
│ ├── __mocks__
│ │ └── utils.ts
│ └── utils.ts
├── axios
│ ├── axiosInstance.ts
│ └── routes.ts
├── db
│ ├── constants.ts
│ ├── data
│ │ ├── bandData.ts
│ │ └── generateData.ts
│ └── db-utils.ts
├── features
│ ├── bands
│ │ ├── queries.ts
│ │ └── types.ts
│ ├── reservations
│ │ ├── queries.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ ├── shows
│ │ ├── queries.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ └── users
│ │ ├── queries.ts
│ │ ├── types.ts
│ │ ├── useSessionStatus.ts
│ │ └── utils.ts
└── theme
│ └── index.ts
├── next-env.d.ts
├── package-lock.json
├── package.json
├── pages
├── _app.tsx
├── _document.tsx
├── api
│ ├── auth
│ │ └── [...nextauth].ts
│ ├── bands
│ │ └── index.ts
│ ├── reservations
│ │ └── [reservationId].ts
│ ├── revalidate
│ │ └── index.ts
│ ├── shows
│ │ ├── [showId].ts
│ │ └── index.ts
│ └── users
│ │ ├── [userId]
│ │ └── reservations.ts
│ │ └── index.ts
├── auth
│ └── signin.tsx
├── bands
│ ├── [bandId].tsx
│ └── index.tsx
├── confirmation
│ └── [reservationId].tsx
├── index.tsx
├── reservations
│ └── [showId].tsx
├── shows
│ └── index.tsx
└── user
│ └── index.tsx
├── public
├── band-images
│ ├── band1.jpg
│ ├── band10.jpg
│ ├── band11.jpg
│ ├── band12.jpg
│ ├── band13.jpg
│ ├── band14.jpg
│ ├── band15.jpg
│ ├── band16.jpg
│ ├── band17.jpg
│ ├── band18.jpg
│ ├── band2.jpg
│ ├── band3.jpg
│ ├── band4.jpg
│ ├── band5.jpg
│ ├── band6.jpg
│ ├── band7.jpg
│ ├── band8.jpg
│ └── band9.jpg
├── favicon.ico
└── splash
│ ├── heart-hands-logo.jpeg
│ └── heart-hands.jpg
├── scripts
└── reset-db.sh
├── tsconfig.json
└── types
├── next-auth.types.d.ts
└── types.d.ts
/README.md:
--------------------------------------------------------------------------------
1 | # udemy-NEXTJS-TESTING
2 |
3 | Code to accompany the [Testing Next.js Applications](https://www.udemy.com/course/nextjs-testing/?couponCode=TEST-NEXTJS-GITHUB) Udemy course
4 |
--------------------------------------------------------------------------------
/base-concert-venue/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
--------------------------------------------------------------------------------
/base-concert-venue/.env.local_template:
--------------------------------------------------------------------------------
1 | # some long, hard-to-guess string for encoding JSON web tokens
2 | NEXTAUTH_SECRET=""
3 |
4 | # another long, hard-to-guess string for encoding revalidation requests
5 | REVALIDATION_SECRET=""
--------------------------------------------------------------------------------
/base-concert-venue/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/base-concert-venue/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/base-concert-venue/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/base-concert-venue/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": "explicit"
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n"
19 | }
20 |
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/__mocks__/fakeData/newShow.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | import { ShowWithoutAvailableSeatCount } from "@/lib/features/shows/types";
4 |
5 | import { generateNewBand } from "./newBand";
6 |
7 | export const generateNewShow = (
8 | showId: number
9 | ): ShowWithoutAvailableSeatCount => {
10 | // note: this band will not exist in the db,
11 | // so link to the band from the /shows page will not work
12 | const band = generateNewBand(showId);
13 | return {
14 | id: showId,
15 | band,
16 | scheduledAt: dayjs("2022-04-18").toDate(),
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | describe("Home", () => {
6 | it("renders a heading", () => {
7 | render();
8 |
9 | const heading = screen.getByRole("heading", {
10 | name: /welcome to next\.js!/i,
11 | });
12 |
13 | expect(heading).toBeInTheDocument();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/base-concert-venue/__tests__/snapshot.tsx:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | it("renders homepage unchanged", () => {
6 | const { container } = render();
7 | expect(container).toMatchSnapshot();
8 | });
9 |
--------------------------------------------------------------------------------
/base-concert-venue/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/base-concert-venue/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/base-concert-venue/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/base-concert-venue/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/base-concert-venue/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/base-concert-venue/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/base-concert-venue/db/bands.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/base-concert-venue/db/reservations.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/base-concert-venue/db/shows.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/base-concert-venue/jest.setup.js:
--------------------------------------------------------------------------------
1 | // Optional: configure or set up a testing framework before each test.
2 | // If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`
3 |
4 | // Used for __tests__/testing-library.js
5 | // Learn more: https://github.com/testing-library/jest-dom
6 | import "@testing-library/jest-dom/extend-expect";
7 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => "db";
5 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/base-concert-venue/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/base-concert-venue/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/base-concert-venue/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/base-concert-venue/pages/api/users/[userId]/reservations.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getReservationsByUserId } from "@/lib/features/users/queries";
5 |
6 | const handler = createHandler({ authRequired: true });
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { userId } = req.query;
9 | const userReservations = await getReservationsByUserId(Number(userId));
10 | return res.status(200).json({ userReservations });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/base-concert-venue/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/favicon.ico
--------------------------------------------------------------------------------
/base-concert-venue/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/base-concert-venue/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/base-concert-venue/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/base-concert-venue/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/base-concert-venue/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/base-concert-venue/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/__mocks__/handlers.ts:
--------------------------------------------------------------------------------
1 | import { http, HttpResponse } from "msw";
2 |
3 | export const handlers = [
4 | http.get("http://localhost:3000/api/description", () => {
5 | return HttpResponse.json({
6 | description: "MSW response",
7 | });
8 | }),
9 | ];
10 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/__mocks__/server.ts:
--------------------------------------------------------------------------------
1 | import { setupServer } from "msw/node";
2 | import { handlers } from "./handlers";
3 |
4 | export const server = setupServer(...handlers);
5 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/global.css";
2 | import type { AppProps } from "next/app";
3 |
4 | export default function MyApp({ Component, pageProps }: AppProps) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/pages/api/description.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | type ResponseData = {
4 | description: string;
5 | };
6 |
7 | export default function handler(
8 | req: NextApiRequest,
9 | res: NextApiResponse
10 | ) {
11 | res.status(200).json({ description: "actual response" });
12 | }
13 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/pages/home/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import Home from ".";
3 |
4 | test("MSW intercepts request", async () => {
5 | render();
6 |
7 | const description = await screen.findByRole("heading", {
8 | name: "MSW response",
9 | });
10 | expect(description).toBeInTheDocument();
11 | });
12 |
--------------------------------------------------------------------------------
/nextjs-msw2-example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/nextjs-msw2-example/public/favicon.ico
--------------------------------------------------------------------------------
/nextjs-msw2-example/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/.env.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": true
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n",
19 | "files.associations": {
20 | ".env*": "env"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/__mocks__/fakeData/newShow.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | import { ShowWithoutAvailableSeatCount } from "@/lib/features/shows/types";
4 |
5 | import { generateNewBand } from "./newBand";
6 |
7 | export const generateNewShow = (
8 | showId: number
9 | ): ShowWithoutAvailableSeatCount => {
10 | // note: this band will not exist in the db,
11 | // so link to the band from the /shows page will not work
12 | const band = generateNewBand(showId);
13 | return {
14 | id: showId,
15 | band,
16 | scheduledAt: dayjs("2022-04-18").toDate(),
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => "db";
5 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/favicon.ico
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-05-testdb/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/section-start-code/section-05-testdb/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.env.local_template:
--------------------------------------------------------------------------------
1 | # some long, hard-to-guess string for encoding JSON web tokens
2 | NEXTAUTH_SECRET=""
3 |
4 | # another long, hard-to-guess string for encoding revalidation requests
5 | REVALIDATION_SECRET=""
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.env.test.local_template:
--------------------------------------------------------------------------------
1 | # some long, hard-to-guess string for encoding JSON web tokens
2 | NEXTAUTH_SECRET=""
3 |
4 | # another long, hard-to-guess string for encoding revalidation requests
5 | REVALIDATION_SECRET=""
6 |
7 | DB_PATH="./__tests__/__mocks__/db"
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": true
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n"
19 | }
20 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/db/bands.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/db/reservations.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/db/shows.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/db/user.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => {
5 | if (!process.env.DB_PATH) {
6 | throw new Error("Missing process.env.DB_PATH");
7 | }
8 |
9 | return process.env.DB_PATH;
10 | };
11 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/favicon.ico
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-06-routes-and-cypress/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/section-start-code/section-06-routes-and-cypress/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/.env.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
12 |
13 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/.env.test.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
12 |
13 | # Base URL for NextAuth
14 | NEXTAUTH_URL="http://localhost:3000"
15 |
16 | DB_PATH="./__tests__/__mocks__/db"
17 |
18 | CYPRESS_baseUrl="http://localhost:3000"
19 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/__mocks__/db/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/__mocks__/db/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/cypress.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/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 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import "@testing-library/cypress/add-commands";
2 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => {
5 | if (!process.env.DB_PATH) {
6 | throw new Error("Missing process.env.DB_PATH");
7 | }
8 |
9 | return process.env.DB_PATH;
10 | };
11 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/favicon.ico
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-07-isr-and-data-updates/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/section-start-code/section-07-isr-and-data-updates/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/.env.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
12 |
13 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
36 | # cypress
37 | cypress/videos
38 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": true
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n",
19 | "files.associations": {
20 | ".env*": "env"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/db/reservations.json:
--------------------------------------------------------------------------------
1 | [{"id":0,"showId":0,"userId":1,"seatCount":4},{"id":1,"showId":0,"userId":100,"seatCount":386},{"id":2,"showId":1,"userId":1,"seatCount":3},{"id":3,"showId":1,"userId":100,"seatCount":397},{"id":4,"showId":2,"userId":100,"seatCount":300},{"id":12345,"showId":0,"userId":0,"seatCount":2}]
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/db/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/fakeData/newShow.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | import { ShowWithoutAvailableSeatCount } from "@/lib/features/shows/types";
4 |
5 | import { generateNewBand } from "./newBand";
6 |
7 | export const generateNewShow = (
8 | showId: number
9 | ): ShowWithoutAvailableSeatCount => {
10 | // note: this band will not exist in the db,
11 | // so link to the band from the /shows page will not work
12 | const band = generateNewBand(showId);
13 | return {
14 | id: showId,
15 | band,
16 | scheduledAt: dayjs("2022-04-18").toDate(),
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/cypress.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/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 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/cypress/integration/shows-isr.test.js:
--------------------------------------------------------------------------------
1 | it("skips client-side bundle, confirming data from ISR cache", () => {
2 | // reference: https://glebbahmutov.com/blog/ssr-e2e/#removing-application-bundle
3 | cy.request("/shows")
4 | .its("body")
5 | .then((html) => {
6 | // remove the application code bundle
7 | const staticHtml = html.replace('', "");
8 | cy.state("document").write(staticHtml);
9 | });
10 |
11 | cy.findAllByText(/2022 apr 1[456]/i).should("have.length.of", 3);
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import "@testing-library/cypress/add-commands";
2 |
3 | Cypress.Commands.add("resetDbAndIsrCache", () => {
4 | cy.task("db:reset");
5 | const secret = Cypress.env("REVALIDATION_SECRET");
6 | cy.request("GET", `/api/revalidate?secret=${secret}`);
7 | });
8 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => {
5 | if (!process.env.DB_PATH) {
6 | throw new Error("Missing process.env.DB_PATH");
7 | }
8 |
9 | return process.env.DB_PATH;
10 | };
11 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/favicon.ico
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-08-auth/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/section-start-code/section-08-auth/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-09-api/.env.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
12 |
13 | DB_PATH="./db"
--------------------------------------------------------------------------------
/section-start-code/section-09-api/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
36 | # cypress
37 | cypress/videos
38 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": true
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n",
19 | "files.associations": {
20 | ".env*": "env"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/db/reservations.json:
--------------------------------------------------------------------------------
1 | [{"id":0,"showId":0,"userId":1,"seatCount":4},{"id":1,"showId":0,"userId":100,"seatCount":386},{"id":2,"showId":1,"userId":1,"seatCount":3},{"id":3,"showId":1,"userId":100,"seatCount":397},{"id":4,"showId":2,"userId":100,"seatCount":300},{"id":58001144,"showId":2,"userId":1,"seatCount":5}]
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/db/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/fakeData/newShow.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | import { ShowWithoutAvailableSeatCount } from "@/lib/features/shows/types";
4 |
5 | import { generateNewBand } from "./newBand";
6 |
7 | export const generateNewShow = (
8 | showId: number
9 | ): ShowWithoutAvailableSeatCount => {
10 | // note: this band will not exist in the db,
11 | // so link to the band from the /shows page will not work
12 | const band = generateNewBand(showId);
13 | return {
14 | id: showId,
15 | band,
16 | scheduledAt: dayjs("2022-04-18").toDate(),
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/cypress.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/cypress/fixtures/protected-pages.json:
--------------------------------------------------------------------------------
1 | ["/reservations/0", "/user"]
2 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/cypress/integration/isr/shows-isr.test.js:
--------------------------------------------------------------------------------
1 | it("skips client-side bundle, confirming data from ISR cache", () => {
2 | // reference: https://glebbahmutov.com/blog/ssr-e2e/#removing-application-bundle
3 | cy.request("/shows")
4 | .its("body")
5 | .then((html) => {
6 | // remove the application code bundle
7 | const staticHtml = html.replace('', "");
8 | cy.state("document").write(staticHtml);
9 | });
10 |
11 | cy.findAllByText(/2022 apr 1[456]/i).should("have.length.of", 3);
12 | });
13 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => {
5 | if (!process.env.DB_PATH) {
6 | throw new Error("Missing process.env.DB_PATH");
7 | }
8 |
9 | return process.env.DB_PATH;
10 | };
11 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/favicon.ico
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/section-start-code/section-09-api/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/section-start-code/section-09-api/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/section-start-code/section-09-api/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/.env.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
12 |
13 | DB_PATH="./db"
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": true
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n",
19 | "files.associations": {
20 | ".env*": "env"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/db/reservations.json:
--------------------------------------------------------------------------------
1 | [{"id":0,"showId":0,"userId":1,"seatCount":4},{"id":1,"showId":0,"userId":100,"seatCount":386},{"id":2,"showId":1,"userId":1,"seatCount":3},{"id":3,"showId":1,"userId":100,"seatCount":397},{"id":4,"showId":2,"userId":100,"seatCount":300}]
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/db/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/cypress/e2e/isr/shows-isr.cy.js:
--------------------------------------------------------------------------------
1 | it("skips client-side bundle, confirming data from ISR cache", () => {
2 | // reference: https://glebbahmutov.com/blog/ssr-e2e/#removing-application-bundle
3 | cy.request("/shows")
4 | .its("body")
5 | .then((html) => {
6 | // remove the scripts, so they don't start automatically
7 | const staticHtml = html.replace(/.*?<\/script>/gm, "");
8 | cy.state("document").write(staticHtml);
9 | });
10 |
11 | cy.findAllByText(/2022 apr 1[456]/i).should("have.length.of", 3);
12 | });
13 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/cypress/fixtures/protected-pages.json:
--------------------------------------------------------------------------------
1 | ["/reservations/0", "/user"]
2 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/auth/__mocks__/utils.ts:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | esModule: true,
3 | validateToken: jest.fn().mockResolvedValue(true),
4 | };
5 |
6 | // to satisfy TypeScript
7 | export {};
8 |
9 | // mock module in test file using this code
10 | // jest.mock("@/lib/auth/utils")
11 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => {
5 | if (!process.env.DB_PATH) {
6 | throw new Error("Missing process.env.DB_PATH");
7 | }
8 |
9 | return process.env.DB_PATH;
10 | };
11 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/favicon.ico
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue-nextjs12.2-cypress10.3/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tested-concert-venue-nextjs12.2-cypress10.3/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------
/tested-concert-venue/.env.development.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL="http://localhost:3000"
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL="http://localhost:3000"
6 |
7 | DB_PATH="./db"
--------------------------------------------------------------------------------
/tested-concert-venue/.env.local_template:
--------------------------------------------------------------------------------
1 | # Base URL for NextAuth
2 | NEXTAUTH_URL=""
3 |
4 | # Base URL for axios
5 | NEXT_PUBLIC_BASE_URL=""
6 |
7 | # some long, hard-to-guess string for encoding JSON web tokens
8 | NEXTAUTH_SECRET=""
9 |
10 | # another long, hard-to-guess string for encoding revalidation requests
11 | REVALIDATION_SECRET=""
12 |
13 | DB_PATH="./db"
--------------------------------------------------------------------------------
/tested-concert-venue/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
36 | # cypress
37 | cypress/videos
38 |
--------------------------------------------------------------------------------
/tested-concert-venue/.npmrc:
--------------------------------------------------------------------------------
1 | fund=false
2 | audit=false
3 | loglevel=error
4 |
--------------------------------------------------------------------------------
/tested-concert-venue/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/tested-concert-venue/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | "typescript",
6 | "typescriptreact"
7 | ],
8 | "[javascript,javascriptreact,typescript,typescriptreact]": {
9 | "editor.codeActionsOnSave": {
10 | "source.fixAll.eslint": "explicit"
11 | }
12 | },
13 | "[json]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.json-language-features"
16 | },
17 | "editor.tabSize": 2,
18 | "files.eol": "\n",
19 | "files.associations": {
20 | ".env*": "env"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/db/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/db/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/fakeData/json/reservations.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "showId": 0,
5 | "userId": 1,
6 | "seatCount": 4
7 | },
8 | {
9 | "id": 1,
10 | "showId": 0,
11 | "userId": 100,
12 | "seatCount": 386
13 | },
14 | {
15 | "id": 2,
16 | "showId": 1,
17 | "userId": 1,
18 | "seatCount": 3
19 | },
20 | {
21 | "id": 3,
22 | "showId": 1,
23 | "userId": 100,
24 | "seatCount": 397
25 | },
26 | {
27 | "id": 4,
28 | "showId": 2,
29 | "userId": 100,
30 | "seatCount": 300
31 | }
32 | ]
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/fakeData/json/users.json:
--------------------------------------------------------------------------------
1 | [{"email":"test@test.test","salt":"tpsQWJ+msIv8w2lkOsnAeA/8Ssv0w927JG+RusiSFDAvL5l2OYhovRyInGn6QxFgLtuuwKbW8c1gwAR1ux7Yjy+9FJRWEb4EoVCMj15L0vUf7M4baiVqxOzZ/Z6uv0TZfrEIMjT3n+0OJ6vIs4lHkQFACkTbyzmVarzxnoErrus=","hash":"��N.$�ͣ��i��tRWY���BJ$c0$���P�;���\u0015\r�;2Ol�\u0014_�*�=t˓\u001av�Pq\\9 7[","iterations":10000,"keylen":64,"digest":"sha512","id":1}]
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/fakeData/newBand.ts:
--------------------------------------------------------------------------------
1 | import { Band } from "@/lib/features/bands/types";
2 |
3 | export const generateNewBand = (bandId: number): Band => ({
4 | id: bandId,
5 | name: "Avalanche of Cheese",
6 | description: "masterful reggae featuring brilliant harmonies",
7 | image: {
8 | fileName: "band11.jpg",
9 | authorName: "Saksham Gangwar",
10 | authorLink: "https://unsplash.com/@saksham",
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/fakeData/newReservation.ts:
--------------------------------------------------------------------------------
1 | import { Reservation } from "@/lib/features/reservations/types";
2 |
3 | export const generateNewReservation = ({
4 | reservationId,
5 | showId,
6 | seatCount,
7 | }: {
8 | reservationId: number;
9 | showId: number;
10 | seatCount: number;
11 | }): Reservation => ({
12 | id: reservationId,
13 | showId,
14 | userId: showId,
15 | seatCount,
16 | });
17 |
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/fakeData/newShow.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | import { ShowWithoutAvailableSeatCount } from "@/lib/features/shows/types";
4 |
5 | import { generateNewBand } from "./newBand";
6 |
7 | export const generateNewShow = (
8 | showId: number
9 | ): ShowWithoutAvailableSeatCount => {
10 | // note: this band will not exist in the db,
11 | // so link to the band from the /shows page will not work
12 | const band = generateNewBand(showId);
13 | return {
14 | id: showId,
15 | band,
16 | scheduledAt: dayjs("2022-04-18").toDate(),
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/__mocks__/msw/server.js:
--------------------------------------------------------------------------------
1 | // src/mocks/server.js
2 | import { setupServer } from "msw/node";
3 |
4 | import { handlers } from "./handlers";
5 |
6 | // This configures a request mocking server with the given request handlers.
7 | export const server = setupServer(...handlers);
8 |
--------------------------------------------------------------------------------
/tested-concert-venue/__tests__/ui/home.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 |
3 | import Home from "@/pages/index";
4 |
5 | test("page has correct heading and image", () => {
6 | render();
7 |
8 | const heading = screen.getByRole("heading", {
9 | name: "Welcome to Popular Concert Venue",
10 | });
11 | expect(heading).toBeInTheDocument();
12 |
13 | const image = screen.getByRole("img", {
14 | name: "Concert goer with hands in the shape of a heart",
15 | });
16 | expect(image).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/tested-concert-venue/components/_common/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@chakra-ui/react";
2 | import React from "react";
3 |
4 | import { NavBar } from "../nav/Nav";
5 | import { LoadingSpinner } from "./LoadingSpinner";
6 |
7 | export const Layout: React.FC = ({ children }) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/tested-concert-venue/components/_common/QueryError.tsx:
--------------------------------------------------------------------------------
1 | import { Heading, Text, VStack } from "@chakra-ui/react";
2 |
3 | export function QueryError({ message }: { message: string }) {
4 | return (
5 |
13 | {message}
14 | please try again later
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/tested-concert-venue/components/auth/SignInError.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 |
3 | export const SignInError = ({ error }: { error: string | Array }) => {
4 | const errors = typeof error === "string" ? [error] : error;
5 |
6 | return (
7 |
8 | {errors.map((errorText) => (
9 | {errorText}
10 | ))}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/tested-concert-venue/components/bands/BandLinkHeading.tsx:
--------------------------------------------------------------------------------
1 | import { Heading } from "@chakra-ui/react";
2 | import Link from "next/link";
3 |
4 | import type { Band } from "@/lib/features/bands/types";
5 |
6 | export const BandLinkHeading = ({ band }: { band: Band }) => (
7 |
8 |
12 | {band.name.toLocaleLowerCase()}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/tested-concert-venue/components/nav/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import Link from "next/link";
3 | import React from "react";
4 |
5 | interface NavLinkProps {
6 | href: string;
7 | children: React.ReactElement | string;
8 | }
9 |
10 | export const NavLink: React.FC = ({ href, children }) => (
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/tested-concert-venue/components/nav/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@chakra-ui/react";
2 | import { signOut } from "next-auth/react";
3 |
4 | import { useSessionStatus } from "@/lib/features/users/useSessionStatus";
5 |
6 | export const SignOutButton: React.FC = () => {
7 | const { isLoading, isLoggedIn } = useSessionStatus();
8 | if (!isLoggedIn) return null;
9 |
10 | const handleClick = () => signOut({ redirect: false });
11 |
12 | return (
13 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/tested-concert-venue/cypress.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tested-concert-venue/cypress/fixtures/protected-pages.json:
--------------------------------------------------------------------------------
1 | ["/reservations/0", "/user"]
2 |
--------------------------------------------------------------------------------
/tested-concert-venue/cypress/integration/isr/shows-isr.test.js:
--------------------------------------------------------------------------------
1 | it("skips client-side bundle, confirming data from ISR cache", () => {
2 | // reference: https://glebbahmutov.com/blog/ssr-e2e/#removing-application-bundle
3 | cy.request("/shows")
4 | .its("body")
5 | .then((html) => {
6 | // remove the scripts, so they don't start automatically
7 | const staticHtml = html.replace(/.*?<\/script>/gm, "");
8 | cy.state("document").write(staticHtml);
9 | });
10 |
11 | cy.findAllByText(/2022 apr 1[456]/i).should("have.length.of", 3);
12 | });
13 |
--------------------------------------------------------------------------------
/tested-concert-venue/cypress/integration/user-page/user.test.js:
--------------------------------------------------------------------------------
1 | it("displays Shows page after clicking 'purchase more tickets' button", () => {
2 | // log in using custom command
3 | cy.task("db:reset").signIn(
4 | Cypress.env("TEST_USER_EMAIL"),
5 | Cypress.env("TEST_PASSWORD")
6 | );
7 |
8 | // access user page
9 | cy.visit("/user");
10 |
11 | // find and click 'purchase more tickets' button
12 | cy.findByRole("button", { name: /purchase more tickets/i }).click();
13 |
14 | // confirm "Shows" page
15 | cy.findByRole("heading", { name: /upcoming shows/i }).should("exist");
16 | });
17 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/auth/__mocks__/utils.ts:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | esModule: true,
3 | validateToken: jest.fn().mockResolvedValue(true),
4 | };
5 |
6 | // to satisfy TypeScript
7 | export {};
8 |
9 | // mock module in test file using this code
10 | // jest.mock("@/lib/auth/utils")
11 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/auth/utils.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest } from "next";
2 | import { getToken } from "next-auth/jwt";
3 |
4 | import { User } from "../features/users/types";
5 |
6 | export const validateToken = async (req: NextApiRequest) => {
7 | const token = await getToken({ req });
8 | if (!token) return false;
9 |
10 | const userId = req.body?.userId ?? req.query?.userId;
11 | if (!userId) return false;
12 |
13 | const tokenUser = token.user as User;
14 | return token && tokenUser.id === Number(userId);
15 | };
16 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/axios/axiosInstance.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from "axios";
2 |
3 | const config: AxiosRequestConfig = {};
4 | export const baseURL =
5 | process.env.NODE_ENV === "test"
6 | ? "http://localhost:3000"
7 | : process.env.NEXT_PUBLIC_BASE_URL;
8 |
9 | config.baseURL = baseURL;
10 |
11 | export const axiosInstance = axios.create(config);
12 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/axios/routes.ts:
--------------------------------------------------------------------------------
1 | export enum routes {
2 | shows = "shows",
3 | users = "users",
4 | user = "user",
5 | bands = "bands",
6 | reservations = "reservations",
7 | images = "images",
8 | }
9 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/db/constants.ts:
--------------------------------------------------------------------------------
1 | export const venueCapacity = 400;
2 |
3 | // this will eventually use environment variables
4 | export const getDbPath = (): string => {
5 | if (!process.env.DB_PATH) {
6 | throw new Error("Missing process.env.DB_PATH");
7 | }
8 |
9 | return process.env.DB_PATH;
10 | };
11 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/bands/types.ts:
--------------------------------------------------------------------------------
1 | export interface Band {
2 | id: number;
3 | name: string;
4 | description: string;
5 | image: Image;
6 | }
7 | export interface Image {
8 | fileName: string;
9 | authorName: string;
10 | authorLink: string;
11 | }
12 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/reservations/types.ts:
--------------------------------------------------------------------------------
1 | import type { Show } from "../shows/types";
2 | // TODO: type vs interface consistency
3 | export interface Reservation {
4 | id: number;
5 | showId: number;
6 | userId: number;
7 | seatCount: number;
8 | }
9 |
10 | export interface ReservationWithShow extends Reservation {
11 | show: Show;
12 | }
13 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/reservations/utils.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomId(): number {
2 | return Math.floor(Math.random() * 100000000);
3 | }
4 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/shows/types.ts:
--------------------------------------------------------------------------------
1 | import type { Band } from "@/lib/features/bands/types";
2 |
3 | export interface ShowWithoutAvailableSeatCount {
4 | id: number;
5 | band: Band;
6 | scheduledAt: Date;
7 | }
8 |
9 | export interface availableSeatCount {
10 | availableSeatCount: number;
11 | }
12 |
13 | export interface Show
14 | extends ShowWithoutAvailableSeatCount,
15 | availableSeatCount {}
16 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/shows/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export function formatDate(dateToFormat: Date): string {
4 | return dayjs(dateToFormat).format("YYYY MMM D").toLowerCase();
5 | }
6 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/users/types.ts:
--------------------------------------------------------------------------------
1 | export interface Id {
2 | id: number;
3 | }
4 |
5 | export interface NewUser {
6 | email: string;
7 | name?: string;
8 | address?: string;
9 | phone?: string;
10 | token?: string;
11 | }
12 |
13 | export interface User extends Id, NewUser {}
14 |
15 | export interface PasswordHash {
16 | salt: string;
17 | hash: string;
18 | iterations: number;
19 | keylen: number;
20 | digest: string;
21 | }
22 |
23 | export interface NewAuthUser extends NewUser, PasswordHash {}
24 | export interface AuthUser extends User, PasswordHash {}
25 |
--------------------------------------------------------------------------------
/tested-concert-venue/lib/features/users/useSessionStatus.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from "next-auth/react";
2 |
3 | interface SessionStatuses {
4 | isLoggedIn: boolean;
5 | isLoading: boolean;
6 | }
7 |
8 | export const useSessionStatus = (): SessionStatuses => {
9 | const { status } = useSession();
10 | return {
11 | isLoading: status === "loading",
12 | isLoggedIn: status === "authenticated",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/tested-concert-venue/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/tested-concert-venue/pages/api/shows/[showId].ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getShowById } from "@/lib/features/shows/queries";
5 |
6 | const handler = createHandler();
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { showId } = req.query;
9 | const show = await getShowById(Number(showId));
10 | return res.status(200).json({ show });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/tested-concert-venue/pages/api/users/[userId]/reservations.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 |
3 | import { createHandler } from "@/lib/api/handler";
4 | import { getReservationsByUserId } from "@/lib/features/users/queries";
5 |
6 | const handler = createHandler({ authRequired: true });
7 | handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
8 | const { userId } = req.query;
9 | const userReservations = await getReservationsByUserId(Number(userId));
10 | return res.status(200).json({ userReservations });
11 | });
12 |
13 | export default handler;
14 |
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band1.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band10.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band11.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band12.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band13.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band14.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band15.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band16.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band17.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band18.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band2.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band3.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band4.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band5.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band6.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band7.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band8.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/band-images/band9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/band-images/band9.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/favicon.ico
--------------------------------------------------------------------------------
/tested-concert-venue/public/splash/heart-hands-logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/splash/heart-hands-logo.jpeg
--------------------------------------------------------------------------------
/tested-concert-venue/public/splash/heart-hands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnie/udemy-NEXTJS-TESTING/7e4ed8371929e06ed198eb2103b59b7ab5774ba4/tested-concert-venue/public/splash/heart-hands.jpg
--------------------------------------------------------------------------------
/tested-concert-venue/scripts/reset-db.sh:
--------------------------------------------------------------------------------
1 | cp -r __tests__/__mocks__/fakeData/json/*.json __tests__/__mocks__/db
2 |
--------------------------------------------------------------------------------
/tested-concert-venue/types/next-auth.types.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import NextAuth from "next-auth";
3 |
4 | import type { User } from "@/lib/features/users/types";
5 |
6 | declare module "next-auth" {
7 | /**
8 | * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
9 | */
10 | interface Session {
11 | user: User;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tested-concert-venue/types/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*module.css" {
2 | const styles: {
3 | [className: string]: string;
4 | };
5 | export default styles;
6 | }
7 |
--------------------------------------------------------------------------------