Skip to content

Commit 4c5b66d

Browse files
committed
test: try to make e2e tests less flakey
1 parent e9085ca commit 4c5b66d

4 files changed

Lines changed: 54 additions & 27 deletions

File tree

test/e2e/create-command.spec.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { expect, test } from '@nuxt/test-utils/playwright'
22

33
test.describe('Create Command', () => {
4+
// TODO: these tests depend on external npm registry API - we should add data fixtures
5+
test.describe.configure({ retries: 2 })
6+
47
test.describe('Visibility', () => {
58
test('/vite - should show create command (same maintainers)', async ({ page, goto }) => {
69
await goto('/vite', { waitUntil: 'domcontentloaded' })
@@ -80,13 +83,15 @@ test.describe('Create Command', () => {
8083
test('hovering create command shows copy button', async ({ page, goto }) => {
8184
await goto('/vite', { waitUntil: 'hydration' })
8285

83-
// Wait for package analysis API to load (create command requires this)
84-
// First ensure the package page has loaded
85-
await expect(page.locator('h1')).toContainText('vite')
86+
await expect(page.locator('h1')).toContainText('vite', { timeout: 15000 })
87+
88+
await expect(page.locator('main header').locator('text=/v\\d+\\.\\d+/')).toBeVisible({
89+
timeout: 15000,
90+
})
8691

8792
// Find the create command container (wait longer for API response)
8893
const createCommandContainer = page.locator('.group\\/createcmd').first()
89-
await expect(createCommandContainer).toBeVisible({ timeout: 15000 })
94+
await expect(createCommandContainer).toBeVisible({ timeout: 20000 })
9095

9196
// Copy button should initially be hidden (opacity-0)
9297
const copyButton = createCommandContainer.locator('button')
@@ -108,9 +113,15 @@ test.describe('Create Command', () => {
108113
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
109114

110115
await goto('/vite', { waitUntil: 'hydration' })
116+
await expect(page.locator('h1')).toContainText('vite', { timeout: 15000 })
117+
118+
await expect(page.locator('main header').locator('text=/v\\d+\\.\\d+/')).toBeVisible({
119+
timeout: 15000,
120+
})
111121

112-
// Find and hover over the create command container
113122
const createCommandContainer = page.locator('.group\\/createcmd').first()
123+
await expect(createCommandContainer).toBeVisible({ timeout: 20000 })
124+
114125
await createCommandContainer.hover()
115126

116127
// Click the copy button

test/e2e/interactions.spec.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
11
import { expect, test } from '@nuxt/test-utils/playwright'
22

33
test.describe('Search Pages', () => {
4+
// TODO: these tests depend on external npm registry API - we should add data fixtures
5+
test.describe.configure({ retries: 2 })
46
test('/search?q=vue → keyboard navigation (arrow keys + enter)', async ({ page, goto }) => {
5-
await goto('/search?q=vue', { waitUntil: 'domcontentloaded' })
7+
await goto('/search?q=vue', { waitUntil: 'hydration' })
68

7-
await expect(page.locator('text=/found \\d+/i')).toBeVisible()
9+
await expect(page.locator('text=/found \\d+|showing \\d+/i').first()).toBeVisible({
10+
timeout: 15000,
11+
})
812

913
const firstResult = page.locator('[data-result-index="0"]').first()
1014
await expect(firstResult).toBeVisible()
1115

12-
const searchInput = page.locator('input[type="search"]')
13-
14-
// ArrowDown changes visual selection but keeps focus in input
16+
// Global keyboard navigation works regardless of focus
17+
// ArrowDown selects the next result
1518
await page.keyboard.press('ArrowDown')
16-
await expect(searchInput).toBeFocused()
1719

18-
// ArrowUp goes back to first result
20+
// ArrowUp selects the previous result
1921
await page.keyboard.press('ArrowUp')
20-
await expect(searchInput).toBeFocused()
2122

22-
// First result is selected, Enter navigates to it
23+
// Enter navigates to the selected result
2324
// URL is /vue not /package/vue (cleaner URLs)
2425
await page.keyboard.press('Enter')
2526
await expect(page).toHaveURL(/\/vue/)
2627
})
2728

2829
test('/search?q=vue → "/" focuses the search input from results', async ({ page, goto }) => {
29-
await goto('/search?q=vue', { waitUntil: 'domcontentloaded' })
30+
await goto('/search?q=vue', { waitUntil: 'hydration' })
3031

31-
await expect(page.locator('text=/found \\d+/i')).toBeVisible()
32+
await expect(page.locator('text=/found \\d+|showing \\d+/i').first()).toBeVisible({
33+
timeout: 15000,
34+
})
3235

3336
await page.locator('[data-result-index="0"]').first().focus()
3437
await page.keyboard.press('/')
3538
await expect(page.locator('input[type="search"]')).toBeFocused()
3639
})
3740

3841
test('/ (homepage) → search, keeps focus on search input', async ({ page, goto }) => {
39-
await goto('/', { waitUntil: 'domcontentloaded' })
42+
await goto('/', { waitUntil: 'hydration' })
4043

4144
const homeSearchInput = page.locator('#home-search')
4245
await homeSearchInput.click()
4346
await page.keyboard.type('vue')
4447

4548
// Wait for navigation to /search (debounce is 250ms)
4649
await expect(page).toHaveURL(/\/search/, { timeout: 10000 })
47-
await expect(page.locator('text=/found \\d+/i')).toBeVisible({ timeout: 15000 })
50+
51+
await expect(page.locator('[data-result-index="0"]').first()).toBeVisible({ timeout: 15000 })
4852

4953
// Home search input should be gone (we're on /search now)
5054
await expect(homeSearchInput).not.toBeVisible()
@@ -56,15 +60,19 @@ test.describe('Search Pages', () => {
5660
})
5761

5862
test('/settings → search, keeps focus on search input', async ({ page, goto }) => {
59-
await goto('/settings', { waitUntil: 'domcontentloaded' })
63+
await goto('/settings', { waitUntil: 'hydration' })
6064

6165
const searchInput = page.locator('input[type="search"]')
66+
await expect(searchInput).toBeVisible()
67+
68+
await searchInput.click()
6269
await searchInput.fill('vue')
6370

64-
await page.waitForURL(/\/search/)
71+
await expect(page).toHaveURL(/\/search/, { timeout: 10000 })
6572

66-
await expect(page.locator('text=/found \\d+/i')).toBeVisible()
73+
await expect(page.locator('[data-result-index="0"]').first()).toBeVisible({ timeout: 15000 })
6774

68-
await expect(searchInput).toBeFocused()
75+
const headerSearchInput = page.locator('#header-search')
76+
await expect(headerSearchInput).toBeFocused()
6977
})
7078
})

test/e2e/package-manager-select.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ import { expect, test } from '@nuxt/test-utils/playwright'
22

33
test.describe('Package Page', () => {
44
test('/vue → package manager select dropdown works', async ({ page, goto }) => {
5-
await goto('/vue', { waitUntil: 'domcontentloaded' })
5+
await goto('/vue', { waitUntil: 'hydration' })
6+
7+
await expect(page.locator('h1')).toContainText('vue', { timeout: 15000 })
68

79
const packageManagerButton = page.locator('button[aria-haspopup="listbox"]').first()
810
await expect(packageManagerButton).toBeVisible()
911

1012
// Open dropdown
1113
await packageManagerButton.click()
1214
const packageManagerDropdown = page.locator('[role="listbox"]')
13-
await expect(packageManagerDropdown).toBeVisible()
15+
await expect(packageManagerDropdown).toBeVisible({ timeout: 5000 })
1416

1517
// Arrow keys navigate the listbox
1618
await packageManagerButton.press('ArrowDown')
@@ -26,6 +28,7 @@ test.describe('Package Page', () => {
2628

2729
// Enter selects option and closes dropdown
2830
await packageManagerButton.click()
31+
await expect(packageManagerDropdown).toBeVisible({ timeout: 5000 })
2932
await packageManagerButton.press('ArrowDown')
3033
await packageManagerButton.press('Enter')
3134
await expect(packageManagerDropdown).not.toBeVisible()

test/e2e/url-compatibility.spec.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { expect, test } from '@nuxt/test-utils/playwright'
22

33
test.describe('npmjs.com URL Compatibility', () => {
4+
// TODO: these tests depend on external npm registry API - we should add data fixtures
5+
test.describe.configure({ retries: 2 })
6+
47
test.describe('Package Pages', () => {
58
test('/package/vue → package page', async ({ page, goto }) => {
69
await goto('/package/vue', { waitUntil: 'domcontentloaded' })
@@ -73,12 +76,14 @@ test.describe('npmjs.com URL Compatibility', () => {
7376

7477
test.describe('User Profile Pages', () => {
7578
test('/~sindresorhus → user profile', async ({ page, goto }) => {
76-
await goto('/~sindresorhus', { waitUntil: 'domcontentloaded' })
79+
await goto('/~sindresorhus', { waitUntil: 'hydration' })
7780

7881
// Should show username
7982
await expect(page.locator('h1')).toContainText('~sindresorhus')
80-
// Should show packages heading (user has packages)
81-
await expect(page.getByRole('heading', { name: 'Packages' })).toBeVisible()
83+
84+
await expect(page.locator('text=/\\d+\\s+public\\s+package/i').first()).toBeVisible({
85+
timeout: 15000,
86+
})
8287
})
8388

8489
test('/~nonexistent-user-12345 → empty user handling', async ({ page, goto }) => {

0 commit comments

Comments
 (0)