forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import { expect, test } from './test-utils'
2
3test.describe('API Documentation Pages', () => {
4 test('docs page loads and shows content for a package', async ({ page, goto }) => {
5 // Use a small, stable package with TypeScript types
6 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'networkidle' })
7
8 // Page title should include package name
9 await expect(page).toHaveTitle(/ufo.*docs/i)
10
11 // Header should show package name and version
12 await expect(page.locator('header').getByText('ufo')).toBeVisible()
13 await expect(page.locator('header').getByText('1.6.3')).toBeVisible()
14
15 // API Docs badge should be visible
16 await expect(page.locator('text=API Docs')).toBeVisible()
17
18 // Should have documentation content
19 const docsContent = page.locator('.docs-content')
20 await expect(docsContent).toBeVisible()
21
22 // Should have at least one function documented
23 await expect(page.locator('.docs-badge--function').first()).toBeVisible()
24 })
25
26 test('docs page shows TOC sidebar on desktop', async ({ page, goto }) => {
27 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'networkidle' })
28
29 // TOC sidebar should be visible (on desktop viewport)
30 const tocSidebar = page.locator('aside')
31 await expect(tocSidebar).toBeVisible()
32
33 // Should have "Contents" heading
34 await expect(tocSidebar.getByText('Contents')).toBeVisible()
35
36 // Should have section links (Functions, etc.)
37 await expect(tocSidebar.locator('a[href="#section-function"]')).toBeVisible()
38 })
39
40 test('TOC links navigate to sections', async ({ page, goto }) => {
41 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'networkidle' })
42
43 // Click on Functions in TOC
44 const functionsLink = page.locator('aside a[href="#section-function"]')
45 await functionsLink.click()
46
47 // URL should have the hash
48 await expect(page).toHaveURL(/#section-function/)
49
50 // Section should be scrolled into view
51 const functionSection = page.locator('#section-function')
52 await expect(functionSection).toBeInViewport()
53 })
54
55 test('clicking symbol name scrolls to symbol', async ({ page, goto }) => {
56 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'networkidle' })
57
58 // Find a symbol link in the TOC
59 const symbolLink = page.locator('aside a[href^="#function-"]').first()
60 const href = await symbolLink.getAttribute('href')
61
62 // Click the symbol link
63 await symbolLink.click()
64
65 // URL should have the hash
66 await expect(page).toHaveURL(new RegExp(href!.replace('#', '#')))
67 })
68
69 test('docs page without version redirects to latest', async ({ page, goto }) => {
70 await goto('/package-docs/ufo', { waitUntil: 'networkidle' })
71
72 // Should redirect to include version
73 await expect(page).toHaveURL(/\/package-docs\/ufo\/v\//)
74 })
75
76 test('package link in header navigates to package page', async ({ page, goto }) => {
77 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'networkidle' })
78
79 // Click on package name in header
80 const packageLink = page.locator('header a').filter({ hasText: 'ufo' })
81 await packageLink.click()
82
83 // Should navigate to package page (URL ends with /ufo)
84 await expect(page).toHaveURL(/\/package\/ufo$/)
85 })
86
87 test('docs page handles package gracefully when types unavailable', async ({ page, goto }) => {
88 // Use a simple JS package - the page should load without crashing
89 // regardless of whether it has types or shows an error state
90 await goto('/package-docs/is-odd/v/3.0.1', { waitUntil: 'networkidle' })
91
92 // Header should always show the package name
93 await expect(page.locator('header').getByText('is-odd')).toBeVisible()
94
95 // Page should be in one of two states:
96 // 1. Shows "not available" / error message
97 // 2. Shows actual docs content (if types were found)
98 const errorState = page.locator('text=/not available|could not generate/i')
99 const docsContent = page.locator('.docs-content')
100
101 // One of these should be visible
102 await expect(errorState.or(docsContent)).toBeVisible()
103 })
104})
105
106test.describe('Version Selector', () => {
107 test('version selector dropdown shows versions', async ({ page, goto }) => {
108 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'hydration' })
109
110 // Find and click the version selector button (wait for it to be visible)
111 const versionButton = page.locator('header button').filter({ hasText: '1.6.3' })
112 await expect(versionButton).toBeVisible({ timeout: 10000 })
113
114 await versionButton.click()
115
116 // Dropdown should appear with version options
117 const dropdown = page.locator('[role="listbox"]')
118 await expect(dropdown).toBeVisible()
119
120 // Should show multiple versions
121 const versionOptions = dropdown.locator('[role="option"]')
122 await expect(versionOptions.first()).toBeVisible()
123 })
124
125 test('selecting a version navigates to that version', async ({ page, goto }) => {
126 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'hydration' })
127
128 // Find and click the version selector button (wait for it to be visible)
129 const versionButton = page.locator('header button').filter({ hasText: '1.6.3' })
130 await expect(versionButton).toBeVisible({ timeout: 10000 })
131
132 await versionButton.click()
133
134 // Find a version link that's not the current version by checking the href
135 const versionLinks = page.locator('[role="option"] a[href*="/package-docs/ufo/v/"]')
136 const count = await versionLinks.count()
137
138 // Find first link that doesn't point to 1.6.3
139 let targetHref: string | null = null
140 for (let i = 0; i < count; i++) {
141 const href = await versionLinks.nth(i).getAttribute('href')
142 if (href && !href.includes('/v/1.6.3')) {
143 targetHref = href
144 await versionLinks.nth(i).click()
145 break
146 }
147 }
148
149 // Skip if no other versions available
150 if (!targetHref) {
151 test.skip()
152 return
153 }
154
155 // URL should match the href we clicked
156 await expect(page).toHaveURL(new RegExp(targetHref.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
157 })
158
159 test('escape key closes version dropdown', async ({ page, goto }) => {
160 await goto('/package-docs/ufo/v/1.6.3', { waitUntil: 'hydration' })
161
162 // Wait for version button to be visible
163 const versionButton = page.locator('header button').filter({ hasText: '1.6.3' })
164 await expect(versionButton).toBeVisible({ timeout: 10000 })
165
166 await versionButton.click()
167
168 const dropdown = page.locator('[role="listbox"]')
169 await expect(dropdown).toBeVisible()
170
171 // Press escape
172 await page.keyboard.press('Escape')
173
174 // Dropdown should close
175 await expect(dropdown).not.toBeVisible()
176 })
177})