├── 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 | --------------------------------------------------------------------------------