Skip to content

Commit

Permalink
yas
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Sep 22, 2024
1 parent fdc8c02 commit 167833b
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 6 deletions.
10 changes: 5 additions & 5 deletions exercises/01.elements/01.solution.reuse/render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ await testStep(

const componentNames = await getComponentCalls(async () => {
fireEvent.click(button)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(componentNames, '🚨 the `Footer` component re-rendered').toEqual([
'App',
])
expect(
componentNames,
'🚨 the `App` component should be the only thing to re-render when clicking the button',
).toEqual(['App'])
},
)

export {}
39 changes: 39 additions & 0 deletions exercises/01.elements/02.solution.props/render.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { getComponentCalls } from '#shared/get-component-calls'
import { testStep, expect, dtl } from '@epic-web/workshop-utils/test'

const { screen, fireEvent } = dtl

await import('./index.tsx')

await testStep('changing the color should re-render the footer', async () => {
const greenButton = await screen.findByRole('button', { name: /green/i })

const componentNames = await getComponentCalls(async () => {
fireEvent.click(greenButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Both `App` and `Footer` components should re-render when changing the color',
).toEqual(expect.arrayContaining(['Main', 'Footer']))
})

await testStep(
'clicking the count button should not re-render the footer',
async () => {
const countButton = await screen.findByRole('button', { name: /count/i })

const componentNames = await getComponentCalls(async () => {
fireEvent.click(countButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 the `Main` component should be the only thing to re-render when clicking the count button',
).toEqual(['Main'])
},
)
61 changes: 61 additions & 0 deletions exercises/01.elements/03.solution.context/render.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { getComponentCalls } from '#shared/get-component-calls'
import { testStep, expect, dtl } from '@epic-web/workshop-utils/test'

const { screen, fireEvent } = dtl

await import('./index.tsx')

await testStep('changing the color should re-render the footer', async () => {
const greenButton = await screen.findByRole('button', { name: /green/i })

const componentNames = await getComponentCalls(async () => {
fireEvent.click(greenButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Both `App` and `Footer` components should re-render when changing the color',
).toEqual(expect.arrayContaining(['App', 'Footer']))
})

await testStep(
'clicking the count button in Main should not re-render the footer',
async () => {
const mainCountButton = await screen.findByRole('button', {
name: /the count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(mainCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `Main` component should re-render when clicking the count button in Main, not the `Footer`',
).toEqual(['Main'])
},
)

await testStep(
'clicking the app count button should not re-render the footer',
async () => {
const appCountButton = await screen.findByRole('button', {
name: /the app count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(appCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `App` component should re-render when clicking the app count button, not the `Footer`',
).toEqual(['App', 'Main'])
},
)
103 changes: 103 additions & 0 deletions exercises/01.elements/04.solution.use-memo/render.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { getComponentCalls } from '#shared/get-component-calls'
import { testStep, expect, dtl } from '@epic-web/workshop-utils/test'

const { screen, fireEvent } = dtl

await import('./index.tsx')

await testStep('changing the color should re-render the footer', async () => {
const greenButton = await screen.findByRole('button', { name: /green/i })

const componentNames = await getComponentCalls(async () => {
fireEvent.click(greenButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Both `App` and `Footer` components should re-render when changing the color',
).toEqual(expect.arrayContaining(['App', 'Footer']))
})

await testStep(
'clicking the count button in Main should not re-render the footer',
async () => {
const mainCountButton = await screen.findByRole('button', {
name: /the count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(mainCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `Main` component should re-render when clicking the count button in Main, not the `Footer`',
).toEqual(['Main'])
},
)

await testStep(
'clicking the app count button should not re-render the footer',
async () => {
const appCountButton = await screen.findByRole('button', {
name: /the app count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(appCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `App` component should re-render when clicking the app count button, not the `Footer`',
).toEqual(['App', 'Main'])
},
)

await testStep('changing the name should re-render the footer', async () => {
const nameInput = await screen.findByLabelText(/name/i)

const componentNames = await getComponentCalls(async () => {
fireEvent.change(nameInput, { target: { value: 'New Name' } })
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Both `App` and `Footer` components should re-render when changing the name',
).toEqual(expect.arrayContaining(['App', 'Footer']))
})

await testStep('footer should display the correct name', async () => {
const nameInput = await screen.findByLabelText(/name/i)
const newName = 'Test User'

fireEvent.change(nameInput, { target: { value: newName } })

const footer = await screen.findByText(new RegExp(newName))
expect(footer).toBeTruthy()
})

await testStep('footer should be memoized', async () => {
const appCountButton = await screen.findByRole('button', {
name: /the app count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(appCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `App` component should re-render when clicking the app count button, not the `Footer`',
).toEqual(['App', 'Main'])
})
12 changes: 12 additions & 0 deletions exercises/01.elements/05.problem.memo/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,15 @@ we've got it rendering unnecessarily again.
it only renders when the `name` or `color` change. Good luck!

[📜 Check out the `memo` docs](https://react.dev/reference/react/memo)

<callout-warning>
The tests rely on the name of your memoized component being `FooterImpl`, like this:

```tsx
const Footer = memo(function FooterImpl(props) {
// ...
})
```

If you name it differently, the tests will fail.
</callout-warning>
2 changes: 1 addition & 1 deletion exercises/01.elements/05.solution.memo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function useColor() {
return color
}

const Footer = memo(function Footer({ name }: { name: string }) {
const Footer = memo(function FooterImpl({ name }: { name: string }) {
const color = useColor()
return (
<footer style={{ color }}>
Expand Down
108 changes: 108 additions & 0 deletions exercises/01.elements/05.solution.memo/render.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { getComponentCalls } from '#shared/get-component-calls'
import { testStep, expect, dtl } from '@epic-web/workshop-utils/test'

const { screen, fireEvent } = dtl

await import('./index.tsx')

await testStep('changing the color should re-render the footer', async () => {
const greenButton = await screen.findByRole('button', { name: /green/i })

const componentNames = await getComponentCalls(async () => {
fireEvent.click(greenButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Both `App` and `FooterImpl` components should re-render when changing the color. Make certain to follow the instructions on how to memoize the `Footer` component and give it a name so these tests can pass.',
).toEqual(expect.arrayContaining(['App', 'FooterImpl']))
})

await testStep(
'clicking the count button in Main should not re-render the footer',
async () => {
const mainCountButton = await screen.findByRole('button', {
name: /the count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(mainCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `Main` component should re-render when clicking the count button in Main, not the `Footer`',
).toEqual(['Main'])
},
)

await testStep(
'clicking the app count button should not re-render the footer',
async () => {
const appCountButton = await screen.findByRole('button', {
name: /the app count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(appCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `App` component should re-render when clicking the app count button, not the `Footer`',
).toEqual(['App', 'Main'])
},
)

await testStep('footer should be memoized', async () => {
const appCountButton = await screen.findByRole('button', {
name: /the app count is/i,
})

const componentNames = await getComponentCalls(async () => {
fireEvent.click(appCountButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 Only the `App` and `Main` components should re-render when clicking the app count button, not the `Footer`',
).toEqual(['App', 'Main'])
})

await testStep('footer should re-render when name changes', async () => {
const nameInput = await screen.findByLabelText(/name/i)

const componentNames = await getComponentCalls(async () => {
fireEvent.change(nameInput, { target: { value: 'New Name' } })
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 The `App`, `Main`, and `FooterImpl` components should re-render when changing the name',
).toEqual(expect.arrayContaining(['App', 'Main', 'FooterImpl']))
})

await testStep('footer should re-render when color changes', async () => {
const blueButton = await screen.findByRole('button', { name: /blue/i })

const componentNames = await getComponentCalls(async () => {
fireEvent.click(blueButton)
// give everything a bit to render
await new Promise((resolve) => setTimeout(resolve, 10))
})

expect(
componentNames,
'🚨 The `App`, `Main`, and `FooterImpl` components should re-render when changing the name',
).toEqual(expect.arrayContaining(['App', 'Main', 'FooterImpl']))
})

0 comments on commit 167833b

Please sign in to comment.