diff --git a/exercises/03.lifting-state/01.solution.lift/controlled-checkbox.test.ts b/exercises/03.lifting-state/01.solution.lift/controlled-checkbox.test.ts new file mode 100644 index 000000000..f0a94a850 --- /dev/null +++ b/exercises/03.lifting-state/01.solution.lift/controlled-checkbox.test.ts @@ -0,0 +1,30 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const dogCheckbox = await testStep( + 'The user can see the dog checkbox', + async () => { + const result = await screen.findByRole('checkbox', { name: /dog/i }) + expect(result).not.toBeChecked() + return result + }, +) + +await testStep('The user can search for a checkbox value', async () => { + fireEvent.change(searchBox, { target: { value: 'dog' } }) +}) + +await testStep('checkbox is checked automatically', async () => { + expect(dogCheckbox).toBeChecked() +}) diff --git a/exercises/03.lifting-state/01.solution.lift/controlled-search.test.ts b/exercises/03.lifting-state/01.solution.lift/controlled-search.test.ts new file mode 100644 index 000000000..6cee02db8 --- /dev/null +++ b/exercises/03.lifting-state/01.solution.lift/controlled-search.test.ts @@ -0,0 +1,43 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const dogCheckbox = await testStep( + 'The user can see the dog checkbox', + async () => { + const result = await screen.findByRole('checkbox', { name: /dog/i }) + expect(result).not.toBeChecked() + return result + }, +) + +await testStep('The user can select the dog checkbox', async () => { + fireEvent.click(dogCheckbox) + expect(dogCheckbox).toBeChecked() +}) + +await testStep( + 'Selecting the checkbox updates the search and results', + async () => { + // Check that the search box value has been updated + expect(searchBox).toHaveValue('dog') + + // Check that the results have been filtered + await dtl.waitFor(async () => { + await screen.findByText(/the joy of owning a dog/i) + + const catResult = screen.queryByText(/caring for your feline friend/i) + expect(catResult).not.toBeInTheDocument() + }) + }, +) diff --git a/exercises/03.lifting-state/01.solution.lift/filtering.test.ts b/exercises/03.lifting-state/01.solution.lift/filtering.test.ts new file mode 100644 index 000000000..dd9eb9f2e --- /dev/null +++ b/exercises/03.lifting-state/01.solution.lift/filtering.test.ts @@ -0,0 +1,30 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const catResult = await testStep('The user can see the results', async () => { + const result = screen.getByText(/caring for your feline friend/i) + expect(result).toBeInTheDocument() + return result +}) + +await testStep('The user can search for a term', async () => { + fireEvent.change(searchBox, { target: { value: 'dog' } }) +}) + +await testStep('The results are filtered', async () => { + await dtl.waitFor(() => { + expect(catResult).not.toBeInTheDocument() + }) + await screen.findByText(/the joy of owning a dog/i) +}) diff --git a/exercises/03.lifting-state/01.solution.lift/like-post.test.ts b/exercises/03.lifting-state/01.solution.lift/like-post.test.ts new file mode 100644 index 000000000..61774cf6a --- /dev/null +++ b/exercises/03.lifting-state/01.solution.lift/like-post.test.ts @@ -0,0 +1,43 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +await testStep('The user can see the posts', async () => { + await screen.findByText(/caring for your feline friend/i) + await screen.findByText(/the joy of owning a dog/i) +}) + +const likeButtons = await testStep( + 'The user can see like buttons', + async () => { + const buttons = await screen.findAllByRole('button', { + name: /add favorite/i, + }) + expect(buttons.length).toBeGreaterThan(0) + return buttons + }, +) + +const totalLikeButtons = likeButtons.length + +await testStep('The user can like a post', async () => { + fireEvent.click(likeButtons[0]) + await screen.findByRole('button', { name: /remove favorite/i }) +}) + +await testStep('The user can unlike a post', async () => { + const unlikeButton = await screen.findByRole('button', { + name: /remove favorite/i, + }) + fireEvent.click(unlikeButton) + await dtl.waitFor(() => + expect( + screen.queryByRole('button', { name: /remove favorite/i }), + ).not.toBeInTheDocument(), + ) + const buttons = await screen.findAllByRole('button', { + name: /add favorite/i, + }) + expect(buttons.length).toBe(totalLikeButtons) +}) diff --git a/exercises/03.lifting-state/01.solution.lift/popstate.test.ts b/exercises/03.lifting-state/01.solution.lift/popstate.test.ts new file mode 100644 index 000000000..963155d79 --- /dev/null +++ b/exercises/03.lifting-state/01.solution.lift/popstate.test.ts @@ -0,0 +1,38 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +const currentPath = window.location.pathname +window.history.pushState({}, '', `${currentPath}?query=dog`) + +await import('./index.tsx') + +await testStep( + 'The search box is initialized with URL query parameter', + async () => { + const searchBox = await screen.findByRole('searchbox', { name: /search/i }) + expect(searchBox).toHaveValue('dog') + }, +) + +// wait for the event handler to be set up +// for some reason it takes a bit +await new Promise(resolve => setTimeout(resolve, 100)) + +await testStep( + 'The search box updates when popstate event is triggered', + async () => { + // Simulate navigation to a new URL + const currentPath = window.location.pathname + window.history.pushState({}, '', `${currentPath}?query=cat`) + + // Trigger popstate event + fireEvent.popState(window) + + // Check if the search box value is updated + await dtl.waitFor(async () => + expect( + await screen.findByRole('searchbox', { name: /search/i }), + ).toHaveValue('cat'), + ) + }, +) diff --git a/exercises/03.lifting-state/01.solution.lift/search-params.test.ts b/exercises/03.lifting-state/01.solution.lift/search-params.test.ts new file mode 100644 index 000000000..9ea83939a --- /dev/null +++ b/exercises/03.lifting-state/01.solution.lift/search-params.test.ts @@ -0,0 +1,14 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +window.history.pushState({}, '', '?query=dog') + +await import('./index.tsx') + +await testStep( + 'The search box is initialized with URL query parameter', + async () => { + const searchBox = await screen.findByRole('searchbox', { name: /search/i }) + expect(searchBox).toHaveValue('dog') + }, +) diff --git a/exercises/03.lifting-state/02.solution.lift-array/controlled-checkbox.test.ts b/exercises/03.lifting-state/02.solution.lift-array/controlled-checkbox.test.ts new file mode 100644 index 000000000..f0a94a850 --- /dev/null +++ b/exercises/03.lifting-state/02.solution.lift-array/controlled-checkbox.test.ts @@ -0,0 +1,30 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const dogCheckbox = await testStep( + 'The user can see the dog checkbox', + async () => { + const result = await screen.findByRole('checkbox', { name: /dog/i }) + expect(result).not.toBeChecked() + return result + }, +) + +await testStep('The user can search for a checkbox value', async () => { + fireEvent.change(searchBox, { target: { value: 'dog' } }) +}) + +await testStep('checkbox is checked automatically', async () => { + expect(dogCheckbox).toBeChecked() +}) diff --git a/exercises/03.lifting-state/02.solution.lift-array/controlled-search.test.ts b/exercises/03.lifting-state/02.solution.lift-array/controlled-search.test.ts new file mode 100644 index 000000000..6cee02db8 --- /dev/null +++ b/exercises/03.lifting-state/02.solution.lift-array/controlled-search.test.ts @@ -0,0 +1,43 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const dogCheckbox = await testStep( + 'The user can see the dog checkbox', + async () => { + const result = await screen.findByRole('checkbox', { name: /dog/i }) + expect(result).not.toBeChecked() + return result + }, +) + +await testStep('The user can select the dog checkbox', async () => { + fireEvent.click(dogCheckbox) + expect(dogCheckbox).toBeChecked() +}) + +await testStep( + 'Selecting the checkbox updates the search and results', + async () => { + // Check that the search box value has been updated + expect(searchBox).toHaveValue('dog') + + // Check that the results have been filtered + await dtl.waitFor(async () => { + await screen.findByText(/the joy of owning a dog/i) + + const catResult = screen.queryByText(/caring for your feline friend/i) + expect(catResult).not.toBeInTheDocument() + }) + }, +) diff --git a/exercises/03.lifting-state/02.solution.lift-array/filtering.test.ts b/exercises/03.lifting-state/02.solution.lift-array/filtering.test.ts new file mode 100644 index 000000000..dd9eb9f2e --- /dev/null +++ b/exercises/03.lifting-state/02.solution.lift-array/filtering.test.ts @@ -0,0 +1,30 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const catResult = await testStep('The user can see the results', async () => { + const result = screen.getByText(/caring for your feline friend/i) + expect(result).toBeInTheDocument() + return result +}) + +await testStep('The user can search for a term', async () => { + fireEvent.change(searchBox, { target: { value: 'dog' } }) +}) + +await testStep('The results are filtered', async () => { + await dtl.waitFor(() => { + expect(catResult).not.toBeInTheDocument() + }) + await screen.findByText(/the joy of owning a dog/i) +}) diff --git a/exercises/03.lifting-state/02.solution.lift-array/like-post.test.ts b/exercises/03.lifting-state/02.solution.lift-array/like-post.test.ts new file mode 100644 index 000000000..2ad1936a4 --- /dev/null +++ b/exercises/03.lifting-state/02.solution.lift-array/like-post.test.ts @@ -0,0 +1,53 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +await testStep('The user can see the posts', async () => { + await screen.findByText(/caring for your feline friend/i) + await screen.findByText(/the joy of owning a dog/i) +}) + +const likeButtons = await testStep( + 'The user can see like buttons', + async () => { + const buttons = await screen.findAllByRole('button', { + name: /add favorite/i, + }) + expect(buttons.length).toBeGreaterThan(0) + return buttons + }, +) + +const totalLikeButtons = likeButtons.length + +await testStep('The user can like a post', async () => { + fireEvent.click(likeButtons[1]) + await screen.findByRole('button', { name: /remove favorite/i }) +}) + +await testStep('The liked post moves to the top', async () => { + const posts = screen.getAllByRole('listitem') + const firstPost = posts[0] + expect( + firstPost, + 'The first post should have a remove favorite button', + ).toContainElement(screen.getByRole('button', { name: /remove favorite/i })) +}) + +await testStep('The user can unlike a post', async () => { + const unlikeButton = await screen.findByRole('button', { + name: /remove favorite/i, + }) + fireEvent.click(unlikeButton) + + await dtl.waitFor(() => + expect( + screen.queryByRole('button', { name: /remove favorite/i }), + ).not.toBeInTheDocument(), + ) + const buttons = await screen.findAllByRole('button', { + name: /add favorite/i, + }) + expect(buttons.length).toBe(totalLikeButtons) +}) diff --git a/exercises/03.lifting-state/02.solution.lift-array/popstate.test.ts b/exercises/03.lifting-state/02.solution.lift-array/popstate.test.ts new file mode 100644 index 000000000..963155d79 --- /dev/null +++ b/exercises/03.lifting-state/02.solution.lift-array/popstate.test.ts @@ -0,0 +1,38 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +const currentPath = window.location.pathname +window.history.pushState({}, '', `${currentPath}?query=dog`) + +await import('./index.tsx') + +await testStep( + 'The search box is initialized with URL query parameter', + async () => { + const searchBox = await screen.findByRole('searchbox', { name: /search/i }) + expect(searchBox).toHaveValue('dog') + }, +) + +// wait for the event handler to be set up +// for some reason it takes a bit +await new Promise(resolve => setTimeout(resolve, 100)) + +await testStep( + 'The search box updates when popstate event is triggered', + async () => { + // Simulate navigation to a new URL + const currentPath = window.location.pathname + window.history.pushState({}, '', `${currentPath}?query=cat`) + + // Trigger popstate event + fireEvent.popState(window) + + // Check if the search box value is updated + await dtl.waitFor(async () => + expect( + await screen.findByRole('searchbox', { name: /search/i }), + ).toHaveValue('cat'), + ) + }, +) diff --git a/exercises/03.lifting-state/02.solution.lift-array/search-params.test.ts b/exercises/03.lifting-state/02.solution.lift-array/search-params.test.ts new file mode 100644 index 000000000..9ea83939a --- /dev/null +++ b/exercises/03.lifting-state/02.solution.lift-array/search-params.test.ts @@ -0,0 +1,14 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +window.history.pushState({}, '', '?query=dog') + +await import('./index.tsx') + +await testStep( + 'The search box is initialized with URL query parameter', + async () => { + const searchBox = await screen.findByRole('searchbox', { name: /search/i }) + expect(searchBox).toHaveValue('dog') + }, +) diff --git a/exercises/03.lifting-state/03.solution.colocate/controlled-checkbox.test.ts b/exercises/03.lifting-state/03.solution.colocate/controlled-checkbox.test.ts new file mode 100644 index 000000000..f0a94a850 --- /dev/null +++ b/exercises/03.lifting-state/03.solution.colocate/controlled-checkbox.test.ts @@ -0,0 +1,30 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const dogCheckbox = await testStep( + 'The user can see the dog checkbox', + async () => { + const result = await screen.findByRole('checkbox', { name: /dog/i }) + expect(result).not.toBeChecked() + return result + }, +) + +await testStep('The user can search for a checkbox value', async () => { + fireEvent.change(searchBox, { target: { value: 'dog' } }) +}) + +await testStep('checkbox is checked automatically', async () => { + expect(dogCheckbox).toBeChecked() +}) diff --git a/exercises/03.lifting-state/03.solution.colocate/controlled-search.test.ts b/exercises/03.lifting-state/03.solution.colocate/controlled-search.test.ts new file mode 100644 index 000000000..6cee02db8 --- /dev/null +++ b/exercises/03.lifting-state/03.solution.colocate/controlled-search.test.ts @@ -0,0 +1,43 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const dogCheckbox = await testStep( + 'The user can see the dog checkbox', + async () => { + const result = await screen.findByRole('checkbox', { name: /dog/i }) + expect(result).not.toBeChecked() + return result + }, +) + +await testStep('The user can select the dog checkbox', async () => { + fireEvent.click(dogCheckbox) + expect(dogCheckbox).toBeChecked() +}) + +await testStep( + 'Selecting the checkbox updates the search and results', + async () => { + // Check that the search box value has been updated + expect(searchBox).toHaveValue('dog') + + // Check that the results have been filtered + await dtl.waitFor(async () => { + await screen.findByText(/the joy of owning a dog/i) + + const catResult = screen.queryByText(/caring for your feline friend/i) + expect(catResult).not.toBeInTheDocument() + }) + }, +) diff --git a/exercises/03.lifting-state/03.solution.colocate/filtering.test.ts b/exercises/03.lifting-state/03.solution.colocate/filtering.test.ts new file mode 100644 index 000000000..dd9eb9f2e --- /dev/null +++ b/exercises/03.lifting-state/03.solution.colocate/filtering.test.ts @@ -0,0 +1,30 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +const searchBox = await testStep( + 'The user can see the search box', + async () => { + const result = await screen.findByRole('searchbox', { name: /search/i }) + expect(result).toHaveValue('') + return result + }, +) + +const catResult = await testStep('The user can see the results', async () => { + const result = screen.getByText(/caring for your feline friend/i) + expect(result).toBeInTheDocument() + return result +}) + +await testStep('The user can search for a term', async () => { + fireEvent.change(searchBox, { target: { value: 'dog' } }) +}) + +await testStep('The results are filtered', async () => { + await dtl.waitFor(() => { + expect(catResult).not.toBeInTheDocument() + }) + await screen.findByText(/the joy of owning a dog/i) +}) diff --git a/exercises/03.lifting-state/03.solution.colocate/like-post.test.ts b/exercises/03.lifting-state/03.solution.colocate/like-post.test.ts new file mode 100644 index 000000000..067042599 --- /dev/null +++ b/exercises/03.lifting-state/03.solution.colocate/like-post.test.ts @@ -0,0 +1,61 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +import './index.tsx' + +await testStep('The user can see the posts', async () => { + await screen.findByText(/caring for your feline friend/i) + await screen.findByText(/the joy of owning a dog/i) +}) + +const likeButtons = await testStep( + 'The user can see like buttons', + async () => { + const buttons = await screen.findAllByRole('button', { + name: /add favorite/i, + }) + expect(buttons.length).toBeGreaterThan(0) + return buttons + }, +) + +const totalLikeButtons = likeButtons.length + +await testStep('The user can like a post', async () => { + fireEvent.click(likeButtons[1]) + await screen.findByRole('button', { name: /remove favorite/i }) +}) + +await testStep('The liked post does not move to the top', async () => { + const posts = screen.getAllByRole('listitem') + const firstPost = posts[0] + const removeFavoriteButton = screen.getByRole('button', { + name: /remove favorite/i, + }) + expect( + firstPost, + 'The first post should have a remove favorite button', + ).not.toContainElement(removeFavoriteButton) + const secondPost = posts[1] + expect( + secondPost, + 'The second post should have a add favorite button', + ).toContainElement(removeFavoriteButton) +}) + +await testStep('The user can unlike a post', async () => { + const unlikeButton = await screen.findByRole('button', { + name: /remove favorite/i, + }) + fireEvent.click(unlikeButton) + + await dtl.waitFor(() => + expect( + screen.queryByRole('button', { name: /remove favorite/i }), + ).not.toBeInTheDocument(), + ) + const buttons = await screen.findAllByRole('button', { + name: /add favorite/i, + }) + expect(buttons.length).toBe(totalLikeButtons) +}) diff --git a/exercises/03.lifting-state/03.solution.colocate/popstate.test.ts b/exercises/03.lifting-state/03.solution.colocate/popstate.test.ts new file mode 100644 index 000000000..963155d79 --- /dev/null +++ b/exercises/03.lifting-state/03.solution.colocate/popstate.test.ts @@ -0,0 +1,38 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +const currentPath = window.location.pathname +window.history.pushState({}, '', `${currentPath}?query=dog`) + +await import('./index.tsx') + +await testStep( + 'The search box is initialized with URL query parameter', + async () => { + const searchBox = await screen.findByRole('searchbox', { name: /search/i }) + expect(searchBox).toHaveValue('dog') + }, +) + +// wait for the event handler to be set up +// for some reason it takes a bit +await new Promise(resolve => setTimeout(resolve, 100)) + +await testStep( + 'The search box updates when popstate event is triggered', + async () => { + // Simulate navigation to a new URL + const currentPath = window.location.pathname + window.history.pushState({}, '', `${currentPath}?query=cat`) + + // Trigger popstate event + fireEvent.popState(window) + + // Check if the search box value is updated + await dtl.waitFor(async () => + expect( + await screen.findByRole('searchbox', { name: /search/i }), + ).toHaveValue('cat'), + ) + }, +) diff --git a/exercises/03.lifting-state/03.solution.colocate/search-params.test.ts b/exercises/03.lifting-state/03.solution.colocate/search-params.test.ts new file mode 100644 index 000000000..9ea83939a --- /dev/null +++ b/exercises/03.lifting-state/03.solution.colocate/search-params.test.ts @@ -0,0 +1,14 @@ +import { expect, testStep, dtl } from '@epic-web/workshop-utils/test' +const { screen, fireEvent } = dtl + +window.history.pushState({}, '', '?query=dog') + +await import('./index.tsx') + +await testStep( + 'The search box is initialized with URL query parameter', + async () => { + const searchBox = await screen.findByRole('searchbox', { name: /search/i }) + expect(searchBox).toHaveValue('dog') + }, +)