Skip to content

Commit

Permalink
feat: install Playwright for comprehensive e2e testing of component i…
Browse files Browse the repository at this point in the history
…nteractions

install Playwright for comprehensive e2e testing of component interactions

#2311
  • Loading branch information
acd02 committed Jul 25, 2024
1 parent 0d94b91 commit 8e06616
Show file tree
Hide file tree
Showing 13 changed files with 440 additions and 6 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Playwright Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ coverage
*.sln
*.sw?
.nx
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
20 changes: 20 additions & 0 deletions e2e/combobox-within-dialog/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { expect, test } from '@playwright/test'

import { BASE_URL } from '../constant'

test('can interact with a combobox within a dialog', async ({ page }) => {
await page.goto(`${BASE_URL}/combobox-within-dialog`)

const openDialogButton = page.getByRole('button', { name: 'Create account' })

await openDialogButton.click()

await page.getByPlaceholder('Pick a book').click()
await page.getByRole('option', { name: 'To Kill a Mockingbird' }).click()

await expect(page.getByRole('combobox', { name: 'books' })).toHaveValue('To Kill a Mockingbird')

// testing that we can also interact with adjacent button
await page.getByRole('button', { name: 'hello' }).click()
await expect(page.getByRole('button', { name: 'clicked' })).toBeVisible()
})
61 changes: 61 additions & 0 deletions e2e/combobox-within-dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Button } from '@spark-ui/button'
import { Combobox } from '@spark-ui/combobox'
import { Dialog } from '@spark-ui/dialog'
import { FormField } from '@spark-ui/form-field'
import React from 'react'

export function ComboboxWithinDialog() {
const [open, setOpen] = React.useState(false)
const [buttonText, setButtonText] = React.useState('hello')

const handleOpenChange = (open: boolean) => {
setOpen(open)
}

return (
<div className="grid h-full place-items-center gap-y-3xl p-lg">
<Dialog open={open} onOpenChange={handleOpenChange}>
<Dialog.Trigger asChild>
<Button>Create account</Button>
</Dialog.Trigger>

<Dialog.Portal>
<Dialog.Overlay />

<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Create account</Dialog.Title>
</Dialog.Header>

<Dialog.Body className="flex flex-col gap-lg">
<FormField name="books">
<FormField.Label className="text-body-2">books</FormField.Label>
<Combobox>
<Combobox.Trigger>
<Combobox.Input aria-label="Book" placeholder="Pick a book" />
<Combobox.ClearButton aria-label="Clear input" />
<Combobox.Disclosure closedLabel="Open popup" openedLabel="Close popup" />
</Combobox.Trigger>
<Combobox.Popover>
<Combobox.Items>
<Combobox.Empty>No results found</Combobox.Empty>
<Combobox.Item value="book-1">To Kill a Mockingbird</Combobox.Item>
<Combobox.Item value="book-2">War and Peace</Combobox.Item>
<Combobox.Item value="book-3">The Idiot</Combobox.Item>
<Combobox.Item value="book-4">A Picture of Dorian Gray</Combobox.Item>
<Combobox.Item value="book-5">1984</Combobox.Item>
<Combobox.Item value="book-6">Pride and Prejudice</Combobox.Item>
</Combobox.Items>
</Combobox.Popover>
</Combobox>
</FormField>
<Button onClick={() => setButtonText('clicked')}>{buttonText}</Button>
</Dialog.Body>

<Dialog.CloseButton aria-label="Close dialog" />
</Dialog.Content>
</Dialog.Portal>
</Dialog>
</div>
)
}
1 change: 1 addition & 0 deletions e2e/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const BASE_URL = 'http://localhost:3002'
21 changes: 21 additions & 0 deletions e2e/dropdown-with-adjacent-buttons/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, test } from '@playwright/test'

import { BASE_URL } from '../constant'

test('can interact with a dropdown that has adjacent buttons', async ({ page }) => {
await page.goto(`${BASE_URL}/dropdown-with-adjacent-buttons`)

const combobox = page.getByRole('combobox', { name: 'Book' })

await combobox.click()
await page.getByRole('option', { name: 'War and Peace' }).click()

await expect(combobox).toHaveText('War and Peace')

// testing that we can also interact with adjacent buttons
await page.getByRole('button', { name: 'hello' }).click()
await expect(page.getByRole('button', { name: 'clicked on first btn' })).toBeVisible()

await page.getByRole('button', { name: 'there' }).click()
await expect(page.getByRole('button', { name: 'clicked on second btn' })).toBeVisible()
})
55 changes: 55 additions & 0 deletions e2e/dropdown-with-adjacent-buttons/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Button } from '@spark-ui/button'
import { Dropdown } from '@spark-ui/dropdown'
import { FormField } from '@spark-ui/form-field'
import React from 'react'

export function DropdownWithAdjacentButtons() {
const [firstBtnText, setFirstBtnText] = React.useState('hello')
const [secondBtnText, setSecondBtnText] = React.useState('there')

return (
<div style={{ paddingBlock: '200px' }}>
<Button className="my-sm" onClick={() => setFirstBtnText('clicked on first btn')}>
{firstBtnText}
</Button>
<FormField name="books">
<FormField.Label className="text-body-2">Books</FormField.Label>
<Dropdown>
<Dropdown.Trigger aria-label="Book">
<Dropdown.LeadingIcon>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
</svg>
</Dropdown.LeadingIcon>
<Dropdown.Value placeholder="Pick a book" />
</Dropdown.Trigger>

<Dropdown.Popover>
<Dropdown.Items>
<Dropdown.Item value="book-1">To Kill a Mockingbird</Dropdown.Item>
<Dropdown.Item value="book-2">War and Peace</Dropdown.Item>
<Dropdown.Item value="book-3">The Idiot</Dropdown.Item>
<Dropdown.Item value="book-4">A Picture of Dorian Gray</Dropdown.Item>
<Dropdown.Item value="book-5">1984</Dropdown.Item>
<Dropdown.Item value="book-6">Pride and Prejudice</Dropdown.Item>
</Dropdown.Items>
</Dropdown.Popover>
</Dropdown>
</FormField>
<Button className="my-sm" onClick={() => setSecondBtnText('clicked on second btn')}>
{secondBtnText}
</Button>
</div>
)
}
12 changes: 12 additions & 0 deletions e2e/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Playwright playground</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
25 changes: 25 additions & 0 deletions e2e/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import '../src/tailwind.css'

import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'

import { ComboboxWithinDialog } from './combobox-within-dialog'
import { DropdownWithAdjacentButtons } from './dropdown-with-adjacent-buttons'

const router = createBrowserRouter([
{
path: 'combobox-within-dialog',
element: <ComboboxWithinDialog />,
},
{
path: 'dropdown-with-adjacent-buttons',
element: <DropdownWithAdjacentButtons />,
},
])

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
)
Loading

0 comments on commit 8e06616

Please sign in to comment.