Adding tests.

main
Thomas Hintz 2 years ago
parent 7b2f5d0228
commit 9ed17d942f

@ -0,0 +1,10 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
setupNodeEvents(on, config) {
// implement node event listeners here
}
},
});

@ -0,0 +1,31 @@
describe('Podcast episode page', () => {
beforeEach(() => {
cy.visit('/podcast/mechanics-of-react-a-beginners-intro-to-react')
})
it('loads correctly', () => {
// Check that the page title is correct
cy.title().should('equal', `Mechanics of React: A Beginner's Intro To React | The React Show`)
// Check that the date is displayed
cy.get('[data-cy=date]').should('contain', 'March 31, 2023')
// Check that the YouTube video is embedded and playable
cy.get('iframe[src^="https://www.youtube.com/embed/"]').should('be.visible')
// Check that the title is correct
cy.get('[data-cy=title]').should('contain', `[87] Mechanics of React: A Beginner's Intro To React`)
// Check that the description is correct
cy.get('[data-cy=description]').should('contain', 'Learn the fundamentals of React')
// Check that the transcript is correct
cy.get('[data-cy=transcript]').should('contain', '00:00:05 Brought to you from occupied Miwok territory by me')
})
it('should play the audio player when the "Listen" button is clicked', () => {
cy.get('[data-cy=audio-player-button]').first().should('have.attr', 'aria-label', 'Play')
cy.get('[data-cy=audio-player-button]').first().click()
cy.get('[data-cy=audio-player-button]').first().should('have.attr', 'aria-label', 'Pause')
})
})

@ -0,0 +1,26 @@
describe('Episodes component', () => {
beforeEach(() => {
cy.visit('/')
})
it.only('should render the component with episodes data', () => {
cy.get('[data-cy=episode-entry]').should('have.length', 15)
cy.get('[data-cy=episode-title]').first().should('contain', `[87] Mechanics of React: A Beginner's Intro To React`)
cy.get('[data-cy=episode-description]').first().should('contain', 'Learn the fundamentals of React by')
cy.get('[data-cy=episode-date]').first().should('contain', 'March 31, 2023')
cy.get('[data-cy=episode-title]').last().should('contain', '[73] A Fundamentally New React: My Journey with React Server Components')
cy.get('[data-cy=episode-description]').last().should('contain', 'React Sever Components are')
})
it('should navigate to the show notes page when the "Show notes" link is clicked', () => {
cy.get('[data-cy=show-notes-link]').first().click()
cy.url().should('include', '/podcast/mechanics-of-react-a-beginners-intro-to-react')
})
it('should navigate to the transcript section when the "Transcript" link is clicked', () => {
cy.get('[data-cy=transcript-link]').last().click()
cy.url().should('include', '/podcast/a-fundamentally-new-react-my-journey-with-react-server-components#transcript')
})
})

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

2329
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -46,6 +46,7 @@
"devDependencies": {
"@tailwindcss/aspect-ratio": "^0.4.2",
"autoprefixer": "^10.4.13",
"cypress": "^12.9.0",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.4"
}

@ -3,64 +3,6 @@ import Link from 'next/link'
import { Container } from '@/components/Container'
import Episodes from '@/components/Episodes'
function Skeleton({ width, height, className, color }) {
const w = width ? '' : 'w-full';
const c = color ? color : 'bg-slate-200';
return (
<div className={`animate-pulse flex ${c} ${w} ${className}`} style={{ width, height }} />
);
};
function EpisodeEntryLoading() {
return (
<div
className="py-10 sm:py-12"
>
<Container>
<div className="flex flex-col items-start w-full">
<div className="order-first font-mono text-sm leading-7 text-slate-500">
<Skeleton height="1.75rem" width="140px" />
</div>
<h2
className="mt-2 text-lg font-bold text-slate-900 w-full"
>
<Skeleton height="1.75rem" />
</h2>
<div className="mt-1 text-base leading-7 text-slate-700 w-full">
<Skeleton height="1rem" className="mt-3" />
<Skeleton height="1rem" className="mt-3" />
<Skeleton height="1rem" className="mt-3" />
</div>
<div className="mt-4 flex items-center gap-4">
<Skeleton width="0.625rem" height="0.625rem" />
<span className="ml-3">
<Skeleton height="1.5rem" width="42.275px" />
</span>
<span
aria-hidden="true"
className="text-sm font-bold text-slate-400"
>
/
</span>
<Skeleton height="1.5rem" width="80.175px" />
</div>
</div>
</Container>
</div>
)
}
function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return (
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
{[0, 1, 2, 3, 4, 5, 6, 7].map((i) => (
<EpisodeEntryLoading key={i} />
))}
</div>
);
}
export const metadata = {
description: "Weekly podcast focused on React, programming, and software engineering."
};

@ -32,7 +32,7 @@ function Transcript({ children }) {
<>
<hr className="my-12 border-gray-200" />
<h2 className="mt-2 text-3xl font-bold text-slate-900" id="transcript">Transcript</h2>
<div className="space-y-4">
<div className="space-y-4" data-cy="transcript">
{children}
</div>
</>
@ -119,12 +119,13 @@ export default async function Page({ params }) {
<div className="flex items-center gap-6">
<PlayButtonClient audioPlayerData={audioPlayerData} size="large" />
<div className="flex flex-col">
<h1 className="mt-2 text-4xl font-bold text-slate-900">
<h1 className="mt-2 text-4xl font-bold text-slate-900" data-cy="title">
[{episode.num}] {episode.title}
</h1>
<FormattedDate
date={date}
className="order-first font-mono text-sm leading-7 text-slate-500"
data-cy="date"
/>
</div>
</div>
@ -142,6 +143,7 @@ export default async function Page({ params }) {
<div
className="prose prose-slate mt-14 [&>h2]:mt-12 [&>h2]:flex [&>h2]:items-center [&>h2]:font-mono [&>h2]:text-sm [&>h2]:font-medium [&>h2]:leading-7 [&>h2]:text-slate-900 [&>h2]:before:mr-3 [&>h2]:before:h-3 [&>h2]:before:w-1.5 [&>h2]:before:rounded-r-full [&>h2]:before:bg-cyan-200 [&>ul]:mt-6 [&>ul]:list-['\2013\20'] [&>ul]:pl-5 [&>h2:nth-of-type(3n+2)]:before:bg-indigo-200 [&>h2:nth-of-type(3n)]:before:bg-violet-200"
dangerouslySetInnerHTML={{ __html: episode.content || 'CONTENT' }}
data-cy="description"
/>
{episode?.transcript && <Suspense fallback={<TranscriptNoPlayer episode={episode} />}>
<TranscriptWithPlayer episode={episode} />

@ -14,20 +14,23 @@ function EpisodeEntry({ episode }) {
<article
aria-labelledby={`episode-${episode.id}-title`}
className="py-10 sm:py-12"
data-cy="episode-entry"
>
<Container>
<div className="flex flex-col items-start">
<h2
id={`episode-${episode.id}-title`}
className="mt-2 text-lg font-bold text-slate-900"
data-cy="episode-title"
>
<Link href={`/podcast/${episode?.slug}`}>[{episode.num}] {episode.title}</Link>
<Link data-cy="show-notes-link" href={`/podcast/${episode?.slug}`}>[{episode.num}] {episode.title}</Link>
</h2>
<FormattedDate
date={date}
className="order-first font-mono text-sm leading-7 text-slate-500"
data-cy="episode-date"
/>
<p className="mt-1 text-base leading-7 text-slate-700">
<p className="mt-1 text-base leading-7 text-slate-700" data-cy="episode-description">
{episode.description}
</p>
<div className="mt-4 flex items-center gap-4">
@ -62,6 +65,7 @@ function EpisodeEntry({ episode }) {
href={`/podcast/${episode.slug}#transcript`}
className="flex items-center text-sm font-bold leading-6 text-pink-500 hover:text-pink-700 active:text-pink-900"
aria-label={`Transcript for episode ${episode.title}`}
data-cy="transcript-link"
>
Transcript
</Link>

@ -34,6 +34,7 @@ export function PlayButton({ player, size = 'large' }) {
)}
onClick={player.toggle}
aria-label={player.playing ? 'Pause' : 'Play'}
data-cy="audio-player-button"
>
<div className="absolute -inset-3 md:hidden" />
{player.playing ? (

Loading…
Cancel
Save