Skip to content

Commit 4490ab9

Browse files
Fix CI test failures and improve test consistency
- Add contract test to verify mock matches real API structure - Mock API in Popup tests to avoid rate limit failures in CI - Use TEST_ISSUES helpers consistently (no hardcoded issue URLs) - Document Visual tests explaining mocking for stability - Remove --reporter=list from CI workflow (now in config) - Update visual snapshot for new issue numbers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6f29eaa commit 4490ab9

3 files changed

Lines changed: 94 additions & 78 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
run: npm run build:chrome
5656

5757
- name: Run tests
58-
run: npm test -- --reporter=list
58+
run: npm test
5959

6060
- name: Upload Chrome extension
6161
uses: actions/upload-artifact@v4

tests/extension.spec.js

Lines changed: 93 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ test.describe('', () => {
134134
expect(extensionId).toBeTruthy();
135135
});
136136

137+
// Contract test: verify GitHub API returns all fields we depend on.
138+
// If this fails, our mocks need updating to match API changes.
139+
// Note: API doesn't return repository.full_name - popup.js parses from url field instead.
140+
test('GitHub API contract', async () => {
141+
const issue = TEST_ISSUES[0];
142+
const url = `https://api.github.com/repos/${issue.owner}/${issue.repo}/issues/${issue.number}`;
143+
const response = await fetch(url);
144+
expect(response.ok).toBe(true);
145+
146+
const data = await response.json();
147+
expect(data).toHaveProperty('title');
148+
expect(data).toHaveProperty('state');
149+
expect(data).toHaveProperty('html_url');
150+
expect(data).toHaveProperty('updated_at');
151+
expect(data).toHaveProperty('comments');
152+
expect(data).toHaveProperty('url'); // Used by popup.js to parse repo name
153+
});
154+
137155
test('bookmark button', async () => {
138156
const bookmarkSelector = '[data-extension-bookmark]';
139157
const headerActionsSelector = '[data-component="PH_Actions"]';
@@ -241,21 +259,28 @@ test.describe('', () => {
241259
});
242260

243261
test('displays bookmarked issues', async () => {
244-
const testBookmarks = {
245-
'microsoft/playwright/issues/1': {
246-
owner: 'microsoft',
247-
repo: 'playwright',
248-
number: 1,
249-
type: 'issues',
250-
bookmarkedAt: Date.now()
251-
}
252-
};
253-
await addBookmarks(context, testBookmarks);
262+
const issue = TEST_ISSUES[0];
263+
await addBookmarks(context, makeBookmark(issue));
254264

255265
const popupPage = await context.newPage();
256-
await popupPage.goto(`chrome-extension://${extensionId}/assets/popup.html`);
257266

258-
await popupPage.waitForTimeout(2000);
267+
// Mock API to avoid rate limits and ensure test stability
268+
await popupPage.route('**/api.github.com/repos/**', async (route) => {
269+
await route.fulfill({
270+
status: 200,
271+
contentType: 'application/json',
272+
body: JSON.stringify({
273+
title: 'Test Issue',
274+
state: 'open',
275+
html_url: issueUrl(issue),
276+
url: `https://api.github.com/repos/${issue.owner}/${issue.repo}/issues/${issue.number}`,
277+
updated_at: '2024-01-15T10:30:00Z',
278+
comments: 5
279+
})
280+
});
281+
});
282+
283+
await popupPage.goto(`chrome-extension://${extensionId}/assets/popup.html`);
259284

260285
const issueItem = popupPage.locator('.issue-item');
261286
await expect(issueItem).toBeVisible({ timeout: 10000 });
@@ -264,18 +289,28 @@ test.describe('', () => {
264289
});
265290

266291
test('removes bookmark when remove button clicked', async () => {
267-
const testBookmarks = {
268-
'microsoft/playwright/issues/1': {
269-
owner: 'microsoft',
270-
repo: 'playwright',
271-
number: 1,
272-
type: 'issues',
273-
bookmarkedAt: Date.now()
274-
}
275-
};
276-
await addBookmarks(context, testBookmarks);
292+
const issue = TEST_ISSUES[0];
293+
const bookmarkKey = `${issue.owner}/${issue.repo}/issues/${issue.number}`;
294+
await addBookmarks(context, makeBookmark(issue));
277295

278296
const popupPage = await context.newPage();
297+
298+
// Mock API to avoid rate limits and ensure test stability
299+
await popupPage.route('**/api.github.com/repos/**', async (route) => {
300+
await route.fulfill({
301+
status: 200,
302+
contentType: 'application/json',
303+
body: JSON.stringify({
304+
title: 'Test Issue',
305+
state: 'open',
306+
html_url: issueUrl(issue),
307+
url: `https://api.github.com/repos/${issue.owner}/${issue.repo}/issues/${issue.number}`,
308+
updated_at: '2024-01-15T10:30:00Z',
309+
comments: 5
310+
})
311+
});
312+
});
313+
279314
await popupPage.goto(`chrome-extension://${extensionId}/assets/popup.html`);
280315

281316
const issueItem = popupPage.locator('.issue-item');
@@ -288,7 +323,7 @@ test.describe('', () => {
288323
await expect(issueItem).not.toBeVisible();
289324

290325
const bookmarks = await getBookmarks(context);
291-
expect(Object.keys(bookmarks)).not.toContain('microsoft/playwright/issues/1');
326+
expect(Object.keys(bookmarks)).not.toContain(bookmarkKey);
292327

293328
await popupPage.close();
294329
});
@@ -618,6 +653,7 @@ test.describe('', () => {
618653

619654
test('token is used in API requests', async () => {
620655
const testToken = 'github_pat_11ABCDEFGHIJKLMNOPQRST_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW';
656+
const issue = TEST_ISSUES[0];
621657

622658
// Store the token
623659
const setupPage = await context.newPage();
@@ -630,16 +666,7 @@ test.describe('', () => {
630666
await setupPage.close();
631667

632668
// Add a bookmark
633-
const testBookmarks = {
634-
'microsoft/playwright/issues/999': {
635-
owner: 'microsoft',
636-
repo: 'playwright',
637-
number: 999,
638-
type: 'issues',
639-
bookmarkedAt: Date.now()
640-
}
641-
};
642-
await addBookmarks(context, testBookmarks);
669+
await addBookmarks(context, makeBookmark(issue));
643670

644671
// Track Authorization header from intercepted requests
645672
let capturedAuthHeader = null;
@@ -650,11 +677,11 @@ test.describe('', () => {
650677
status: 200,
651678
contentType: 'application/json',
652679
body: JSON.stringify({
653-
id: 999,
654-
number: 999,
680+
number: issue.number,
655681
title: 'Test Issue',
656682
state: 'open',
657-
html_url: 'https://github.com/microsoft/playwright/issues/999',
683+
html_url: issueUrl(issue),
684+
url: `https://api.github.com/repos/${issue.owner}/${issue.repo}/issues/${issue.number}`,
658685
updated_at: new Date().toISOString(),
659686
comments: 0
660687
})
@@ -685,6 +712,8 @@ test.describe('', () => {
685712
});
686713

687714
test('requests work without token', async () => {
715+
const issue = TEST_ISSUES[1]; // Use different issue from previous test
716+
688717
// Ensure no token is set
689718
const setupPage = await context.newPage();
690719
await setupPage.goto(`chrome-extension://${extensionId}/assets/options.html`);
@@ -696,16 +725,7 @@ test.describe('', () => {
696725
await setupPage.close();
697726

698727
// Add a bookmark
699-
const testBookmarks = {
700-
'microsoft/playwright/issues/888': {
701-
owner: 'microsoft',
702-
repo: 'playwright',
703-
number: 888,
704-
type: 'issues',
705-
bookmarkedAt: Date.now()
706-
}
707-
};
708-
await addBookmarks(context, testBookmarks);
728+
await addBookmarks(context, makeBookmark(issue));
709729

710730
// Track Authorization header
711731
let capturedAuthHeader = null;
@@ -715,11 +735,11 @@ test.describe('', () => {
715735
status: 200,
716736
contentType: 'application/json',
717737
body: JSON.stringify({
718-
id: 888,
719-
number: 888,
738+
number: issue.number,
720739
title: 'Test Issue Without Token',
721740
state: 'open',
722-
html_url: 'https://github.com/microsoft/playwright/issues/888',
741+
html_url: issueUrl(issue),
742+
url: `https://api.github.com/repos/${issue.owner}/${issue.repo}/issues/${issue.number}`,
723743
updated_at: new Date().toISOString(),
724744
comments: 0
725745
})
@@ -741,50 +761,44 @@ test.describe('', () => {
741761

742762
// ============================================================
743763
// VISUAL REGRESSION - Screenshot comparison for CSS development
764+
//
765+
// These tests use mocked API responses for visual stability:
766+
// - Ensures consistent issue titles, states, and timestamps in screenshots
767+
// - Avoids rate limits that would cause flaky failures in CI
768+
// - Contract test in Basics block verifies mocks match real API structure
744769
// ============================================================
745770

746771
test.describe('Visual', { tag: '@visual' }, () => {
747772
test('popup with issues', async () => {
748-
// Setup mock bookmarks
749-
const testBookmarks = {
750-
'microsoft/playwright/issues/123': {
751-
owner: 'microsoft',
752-
repo: 'playwright',
753-
number: 123,
754-
type: 'issues',
755-
bookmarkedAt: Date.now()
756-
},
757-
'facebook/react/issues/456': {
758-
owner: 'facebook',
759-
repo: 'react',
760-
number: 456,
761-
type: 'issues',
762-
bookmarkedAt: Date.now() - 86400000
763-
}
764-
};
765-
await addBookmarks(context, testBookmarks);
773+
// Use TEST_ISSUES for consistency, with different timestamps for visual variety
774+
const issue1 = TEST_ISSUES[0]; // microsoft/playwright
775+
const issue2 = TEST_ISSUES[2]; // facebook/react
776+
await addBookmarks(context, {
777+
...makeBookmark(issue1),
778+
...makeBookmark(issue2, Date.now() - 86400000)
779+
});
766780

767781
const popupPage = await context.newPage();
768782

769-
// Mock API responses
783+
// Mock API for visual stability - consistent titles/states for screenshot comparison
770784
await popupPage.route('**/api.github.com/repos/**', async (route) => {
771785
const url = route.request().url();
772786
let data = {
773787
title: 'Sample Issue',
774788
state: 'open',
775-
html_url: url,
789+
html_url: issueUrl(issue1),
790+
url: url,
776791
updated_at: '2024-01-15T10:30:00Z',
777-
comments: 5,
778-
repository: { full_name: 'org/repo' }
792+
comments: 5
779793
};
780-
if (url.includes('playwright')) {
794+
if (url.includes(issue1.repo)) {
781795
data.title = 'Add visual regression testing support';
782-
data.repository.full_name = 'microsoft/playwright';
796+
data.html_url = issueUrl(issue1);
783797
data.state = 'open';
784798
data.comments = 12;
785-
} else if (url.includes('react')) {
799+
} else if (url.includes(issue2.repo)) {
786800
data.title = 'Improve hydration performance';
787-
data.repository.full_name = 'facebook/react';
801+
data.html_url = issueUrl(issue2);
788802
data.state = 'closed';
789803
data.comments = 42;
790804
}
@@ -1186,6 +1200,7 @@ test.describe('', () => {
11861200
});
11871201

11881202
// Route REST API with mixed responses: 1 success, 1 404, 1 403
1203+
const successIssue = TEST_ISSUES[0];
11891204
let callCount = 0;
11901205
await authContext.route('**/api.github.com/repos/**', (route) => {
11911206
callCount++;
@@ -1194,9 +1209,10 @@ test.describe('', () => {
11941209
status: 200,
11951210
contentType: 'application/json',
11961211
body: JSON.stringify({
1197-
number: 1,
1212+
number: successIssue.number,
11981213
title: 'Test Issue',
1199-
html_url: 'https://github.com/microsoft/playwright/issues/1',
1214+
html_url: issueUrl(successIssue),
1215+
url: `https://api.github.com/repos/${successIssue.owner}/${successIssue.repo}/issues/${successIssue.number}`,
12001216
state: 'open',
12011217
updated_at: new Date().toISOString(),
12021218
comments: 0
8.85 KB
Loading

0 commit comments

Comments
 (0)