Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ on:
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.17.1
- run: npm install -g pnpm@8.7.1

node-version: 20
- uses: pnpm/action-setup@v4
with:
version: 9
- run: pnpm install
- run: npx playwright install --with-deps
- run: pnpm test || exit 1

- run: pnpm exec playwright install --with-deps
- run: pnpm test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
path: apps/playground/playwright-report/
retention-days: 30
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Library builds with tsup: entry `src/index.ts` → CJS (`dist/index.js`), ESM (`

## Testing

All tests are Playwright E2E tests in `apps/playground/src/tests/`. Test files cover typing, rendering, selections, slot behavior, props, word deletion, autofocus, and onComplete. Tests run across Chromium, Firefox, WebKit, mobile viewports, and Edge.
All tests are Playwright E2E tests in `apps/playground/src/tests/`. Test files cover typing, rendering, selections, slot behavior, props, word deletion, autofocus, and onComplete. Tests run across Chromium, Firefox, WebKit, and mobile viewports.

Set `WINDOWED_TESTS=1` to run tests in headed mode with slow-mo.

Expand Down
2 changes: 1 addition & 1 deletion apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"scripts": {
"dev": "next dev --port=3039",
"lint": "next lint",
"test": "playwright test --retries=3",
"test": "playwright test",
"test:ui": "playwright test --ui"
},
"dependencies": {
Expand Down
10 changes: 0 additions & 10 deletions apps/playground/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,5 @@ export default defineConfig({
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
},

/* Test against branded browsers. */
{
name: 'Microsoft Edge',
use: { ...devices['Desktop Edge'], channel: 'msedge' },
},
{
name: 'Google Chrome',
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
},
],
})
43 changes: 30 additions & 13 deletions apps/playground/src/tests/base.delete-word.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ test.beforeEach(async ({ page }) => {
await page.goto('/base')
})

/**
* Set the input selection directly and wait for the component's mirrored
* selection (mss/mse) to reflect the new position. This avoids the race
* condition where rapid ArrowLeft/Right presses conflict with the component's
* selectionchange handler calling setSelectionRange.
*/
async function setSelectionAndWait(
page: import('@playwright/test').Page,
start: number,
end: number,
) {
const input = page.getByRole('textbox')
await input.evaluate((el: HTMLInputElement, [s, e]) => {
el.setSelectionRange(s, e)
document.dispatchEvent(new Event('selectionchange'))
}, [start, end] as [number, number])
await expect(input).toHaveAttribute('data-input-otp-mss', String(start))
await expect(input).toHaveAttribute('data-input-otp-mse', String(end))
}

test.describe('Backspace', () => {
test('should backspace previous word (even if there is not a selected character)', async ({ page }) => {
const input = page.getByRole('textbox')
Expand All @@ -21,33 +41,30 @@ test.describe('Backspace', () => {
await input.pressSequentially('123456')
await expect(input).toHaveValue('123456')

await input.press('ArrowLeft')
await input.press('ArrowLeft')
// Select char '4' at position [3,4], then delete backward
await setSelectionAndWait(page, 3, 4)
await input.press(`${modifier}+Backspace`)

await expect(input).toHaveValue('12356')
})
})
// Allow flaky
test.describe.configure({ retries: 3 })
test.describe('Delete', () => {
test('should forward-delete character when pressing delete', async ({ page }) => {
test('should forward-delete character when pressing delete', async ({ page }) => {
const input = page.getByRole('textbox')

await input.pressSequentially('123456')
await expect(input).toHaveValue('123456')

// Delete last char (selection is [5,6])
await input.press('Delete')
await expect(input).toHaveValue('12345')
await input.press('ArrowLeft')
await input.press('ArrowLeft')
await input.press('ArrowLeft')
await input.press('ArrowLeft')
await input.press('ArrowLeft')

// Select first char [0,1] and delete it
await setSelectionAndWait(page, 0, 1)
await input.press('Delete')
await expect(input).toHaveValue('2345')
await input.press('ArrowRight')
await input.press('ArrowRight')

// Select char at [2,3] (which is '4') and delete it
await setSelectionAndWait(page, 2, 3)
await input.press('Delete')
await expect(input).toHaveValue('235')
})
Expand Down
33 changes: 18 additions & 15 deletions apps/playground/src/tests/base.selections.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ test.beforeEach(async ({ page }) => {
})

test.describe('Base tests - Selections', () => {
test.skip(
process.env.CI === 'true',
'Breaks in CI as it cannot handle Shift key',
)

test('should replace selected char if another is pressed', async ({
page,
}) => {
Expand All @@ -21,6 +16,24 @@ test.describe('Base tests - Selections', () => {
await input.pressSequentially('1')
await expect(input).toHaveValue('121')
})
test('should replace last char if another one is pressed', async ({
page,
}) => {
const input = page.getByRole('textbox')

await input.pressSequentially('1234567')
await page.waitForTimeout(100)

await expect(input).toHaveValue('123457')
})
})

test.describe('Base tests - Shift Selections', () => {
test.skip(
process.env.CI === 'true',
'Breaks in CI as it cannot handle Shift key',
)

test('should replace multi-selected chars if another is pressed', async ({
page,
}) => {
Expand All @@ -34,14 +47,4 @@ test.describe('Base tests - Selections', () => {
await input.pressSequentially('1')
await expect(input).toHaveValue('1231')
})
test('should replace last char if another one is pressed', async ({
page,
}) => {
const input = page.getByRole('textbox')

await input.pressSequentially('1234567')
await page.waitForTimeout(100)

await expect(input).toHaveValue('123457')
})
})
5 changes: 4 additions & 1 deletion apps/playground/src/tests/base.typing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ test.describe('Base tests - Typing', () => {
test('should prevent typing greater than max length', async ({ page }) => {
const input = page.getByRole('textbox')

await input.pressSequentially('1234567')
await input.pressSequentially('123456')
await expect(input).toHaveValue('123456')

await input.pressSequentially('7')
await expect(input).toHaveValue('123457')
})
})
Loading