Skip to content

Commit a88644f

Browse files
committed
feat: add GitHub contributors fixture
1 parent 4a3b9e4 commit a88644f

3 files changed

Lines changed: 133 additions & 0 deletions

File tree

modules/runtime/server/cache.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const FIXTURE_PATHS = {
2525
user: 'users',
2626
esmHeaders: 'esm-sh:headers',
2727
esmTypes: 'esm-sh:types',
28+
githubContributors: 'github:contributors.json',
2829
} as const
2930

3031
type FixtureType = keyof typeof FIXTURE_PATHS
@@ -156,6 +157,13 @@ function getMockForUrl(url: string): MockResult | null {
156157
return { data: null }
157158
}
158159

160+
// GitHub API - handled via fixtures, return null to use fixture system
161+
// Note: The actual fixture loading is handled in fetchFromFixtures via special case
162+
if (host === 'api.github.com') {
163+
// Return null here so it goes through fetchFromFixtures which handles the fixture loading
164+
return null
165+
}
166+
159167
// esm.sh is handled specially via $fetch.raw override, not here
160168
// Return null to indicate no mock available at the cachedFetch level
161169

@@ -273,6 +281,39 @@ async function handleFastNpmMeta(
273281
return { data: result }
274282
}
275283

284+
/**
285+
* Handle GitHub API requests using fixtures.
286+
*/
287+
async function handleGitHubApi(
288+
url: string,
289+
storage: ReturnType<typeof useStorage>,
290+
): Promise<MockResult | null> {
291+
let urlObj: URL
292+
try {
293+
urlObj = new URL(url)
294+
} catch {
295+
return null
296+
}
297+
298+
const { host, pathname } = urlObj
299+
300+
if (host !== 'api.github.com') return null
301+
302+
// Contributors endpoint: /repos/{owner}/{repo}/contributors
303+
const contributorsMatch = pathname.match(/^\/repos\/([^/]+)\/([^/]+)\/contributors$/)
304+
if (contributorsMatch) {
305+
const contributors = await storage.getItem<unknown[]>(FIXTURE_PATHS.githubContributors)
306+
if (contributors) {
307+
return { data: contributors }
308+
}
309+
// Return empty array if no fixture exists
310+
return { data: [] }
311+
}
312+
313+
// Other GitHub API endpoints can be added here as needed
314+
return null
315+
}
316+
276317
interface FixtureMatchWithVersion extends FixtureMatch {
277318
version?: string // 'latest', a semver version, or undefined for full packument
278319
}
@@ -385,6 +426,13 @@ async function fetchFromFixtures<T>(
385426
return { data: fastNpmMetaResult.data as T, isStale: false, cachedAt: Date.now() }
386427
}
387428

429+
// Check for GitHub API
430+
const githubResult = await handleGitHubApi(url, storage)
431+
if (githubResult) {
432+
if (VERBOSE) process.stdout.write(`[test-fixtures] GitHub API: ${url}\n`)
433+
return { data: githubResult.data as T, isStale: false, cachedAt: Date.now() }
434+
}
435+
388436
const match = matchUrlToFixture(url)
389437

390438
if (!match) {

test/e2e/test-utils.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,48 @@ async function handleGravatarApi(route: Route): Promise<boolean> {
322322
return true
323323
}
324324

325+
/**
326+
* Handle GitHub API requests.
327+
* Returns mock contributor data for the contributors endpoint.
328+
*/
329+
async function handleGitHubApi(route: Route): Promise<boolean> {
330+
const url = new URL(route.request().url())
331+
const pathname = url.pathname
332+
333+
// Contributors endpoint: /repos/{owner}/{repo}/contributors
334+
const contributorsMatch = pathname.match(/^\/repos\/([^/]+)\/([^/]+)\/contributors$/)
335+
if (contributorsMatch) {
336+
await route.fulfill({
337+
json: [
338+
{
339+
login: 'danielroe',
340+
id: 28706372,
341+
avatar_url: 'https://avatars.githubusercontent.com/u/28706372?v=4',
342+
html_url: 'https://github.com/danielroe',
343+
contributions: 150,
344+
},
345+
{
346+
login: 'antfu',
347+
id: 11247099,
348+
avatar_url: 'https://avatars.githubusercontent.com/u/11247099?v=4',
349+
html_url: 'https://github.com/antfu',
350+
contributions: 120,
351+
},
352+
{
353+
login: 'pi0',
354+
id: 5158436,
355+
avatar_url: 'https://avatars.githubusercontent.com/u/5158436?v=4',
356+
html_url: 'https://github.com/pi0',
357+
contributions: 100,
358+
},
359+
],
360+
})
361+
return true
362+
}
363+
364+
return false
365+
}
366+
325367
/**
326368
* Fail the test with a clear error message when an external API request isn't mocked.
327369
*/
@@ -402,6 +444,12 @@ async function setupRouteMocking(page: Page): Promise<void> {
402444
const handled = await handleGravatarApi(route)
403445
if (!handled) failUnmockedRequest(route, 'Gravatar API')
404446
})
447+
448+
// GitHub API for contributors, etc.
449+
await page.route('https://api.github.com/**', async route => {
450+
const handled = await handleGitHubApi(route)
451+
if (!handled) failUnmockedRequest(route, 'GitHub API')
452+
})
405453
}
406454

407455
/**
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[
2+
{
3+
"login": "danielroe",
4+
"id": 28706372,
5+
"avatar_url": "https://avatars.githubusercontent.com/u/28706372?v=4",
6+
"html_url": "https://github.com/danielroe",
7+
"contributions": 150
8+
},
9+
{
10+
"login": "antfu",
11+
"id": 11247099,
12+
"avatar_url": "https://avatars.githubusercontent.com/u/11247099?v=4",
13+
"html_url": "https://github.com/antfu",
14+
"contributions": 120
15+
},
16+
{
17+
"login": "pi0",
18+
"id": 5158436,
19+
"avatar_url": "https://avatars.githubusercontent.com/u/5158436?v=4",
20+
"html_url": "https://github.com/pi0",
21+
"contributions": 100
22+
},
23+
{
24+
"login": "qwerzl",
25+
"id": 73090488,
26+
"avatar_url": "https://avatars.githubusercontent.com/u/73090488?v=4",
27+
"html_url": "https://github.com/qwerzl",
28+
"contributions": 80
29+
},
30+
{
31+
"login": "harlan-zw",
32+
"id": 5326365,
33+
"avatar_url": "https://avatars.githubusercontent.com/u/5326365?v=4",
34+
"html_url": "https://github.com/harlan-zw",
35+
"contributions": 60
36+
}
37+
]

0 commit comments

Comments
 (0)