Skip to content

Commit c699301

Browse files
fix(routeFromHAR, take 2): hack harRouter to merge set-cookie headers (#38487)
1 parent f4cbaa6 commit c699301

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

packages/playwright-core/src/client/harRouter.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,28 @@ export class HarRouter {
6868
// test when HAR was recorded but we'd abort it immediately.
6969
if (response.status === -1)
7070
return;
71+
72+
73+
// route.fulfill does not support multiple set-cookie headers. We need to merge them into one.
74+
const transformedHeaders = response.headers!.reduce((headersMap, { name, value }) => {
75+
if (name.toLowerCase() !== 'set-cookie') {
76+
// non-set-cookie header gets set as-is
77+
headersMap[name] = value;
78+
} else {
79+
// first set-cookie header gets included as-is
80+
if (!headersMap['set-cookie'])
81+
headersMap['set-cookie'] = value;
82+
else
83+
// subsequent set-cookie headers get appended to existing header
84+
headersMap['set-cookie'] += `\n${value}`;
85+
86+
}
87+
return headersMap;
88+
}, {} as Record<string, string>);
89+
7190
await route.fulfill({
7291
status: response.status,
73-
headers: Object.fromEntries(response.headers!.map(h => [h.name, h.value])),
92+
headers: transformedHeaders,
7493
body: response.body!
7594
});
7695
return;

tests/library/browsercontext-har.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,59 @@ it('should ignore boundary when matching multipart/form-data body', {
452452
await expect(page2.locator('div')).toHaveText('done');
453453
});
454454

455+
it('should record single set-cookie headers', {
456+
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31495' }
457+
}, async ({ contextFactory, server }, testInfo) => {
458+
server.setRoute('/empty.html', (req, res) => {
459+
res.setHeader('Content-Type', 'text/html');
460+
res.setHeader('set-cookie', ['first=foo']);
461+
res.end();
462+
});
463+
464+
const harPath = testInfo.outputPath('har.zip');
465+
const context1 = await contextFactory();
466+
await context1.routeFromHAR(harPath, { update: true });
467+
const page1 = await context1.newPage();
468+
await page1.goto(server.EMPTY_PAGE);
469+
const cookie1 = await page1.evaluate(() => document.cookie);
470+
expect(cookie1).toBe('first=foo');
471+
await context1.close();
472+
473+
const context2 = await contextFactory();
474+
await context2.routeFromHAR(harPath, { notFound: 'abort' });
475+
const page2 = await context2.newPage();
476+
await page2.goto(server.EMPTY_PAGE);
477+
const cookie2 = await page2.evaluate(() => document.cookie);
478+
expect(cookie2).toBe('first=foo');
479+
});
480+
481+
it('should record multiple set-cookie headers', {
482+
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31495' }
483+
}, async ({ contextFactory, server }, testInfo) => {
484+
server.setRoute('/empty.html', (req, res) => {
485+
res.setHeader('Content-Type', 'text/html');
486+
res.setHeader('set-cookie', ['first=foo', 'second=bar']);
487+
res.end();
488+
});
489+
490+
const harPath = testInfo.outputPath('har.zip');
491+
const context1 = await contextFactory();
492+
await context1.routeFromHAR(harPath, { update: true });
493+
const page1 = await context1.newPage();
494+
await page1.goto(server.EMPTY_PAGE);
495+
const cookie1 = await page1.evaluate(() => document.cookie);
496+
expect(cookie1.split('; ').sort().join('; ')).toBe('first=foo; second=bar');
497+
await context1.close();
498+
499+
const context2 = await contextFactory();
500+
await context2.routeFromHAR(harPath, { notFound: 'abort' });
501+
const page2 = await context2.newPage();
502+
await page2.goto(server.EMPTY_PAGE);
503+
const cookie2 = await page2.evaluate(() => document.cookie);
504+
expect(cookie2.split('; ').sort().join('; ')).toBe('first=foo; second=bar');
505+
});
506+
507+
455508
it('should update har.zip for page', async ({ contextFactory, server }, testInfo) => {
456509
const harPath = testInfo.outputPath('har.zip');
457510
const context1 = await contextFactory();

0 commit comments

Comments
 (0)