Skip to content

Commit

Permalink
feat: respond multiple headers with same key (#8183)
Browse files Browse the repository at this point in the history
  • Loading branch information
Junyan committed Apr 5, 2022
1 parent 24e1469 commit c1dcd85
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
2 changes: 1 addition & 1 deletion docs/api.md
Expand Up @@ -5122,7 +5122,7 @@ ResourceType will be one of the following: `document`, `stylesheet`, `image`, `m

- `response` <[Object]> Response that will fulfill this request
- `status` <[number]> Response status code, defaults to `200`.
- `headers` <[Object]> Optional response headers. Header values will be converted to a string.
- `headers` <[Object]> Optional response headers. Header values should be a string or a string array.
- `contentType` <[string]> If set, equals to setting `Content-Type` response header
- `body` <[string]|[Buffer]> Optional response body
- `priority` <[number]> - Optional intercept abort priority. If provided, intercept will be resolved using coopeative handling rules. Otherwise, intercept will be resolved immediately.
Expand Down
24 changes: 16 additions & 8 deletions src/common/HTTPRequest.ts
Expand Up @@ -555,12 +555,15 @@ export class HTTPRequest {
? Buffer.from(response.body)
: (response.body as Buffer) || null;

const responseHeaders: Record<string, string> = {};
const responseHeaders: Record<string, string | string[]> = {};
if (response.headers) {
for (const header of Object.keys(response.headers))
responseHeaders[header.toLowerCase()] = String(
response.headers[header]
);
for (const header of Object.keys(response.headers)) {
const value = response.headers[header];

responseHeaders[header.toLowerCase()] = Array.isArray(value)
? value.map((item) => String(item))
: String(value);
}
}
if (response.contentType)
responseHeaders['content-type'] = response.contentType;
Expand Down Expand Up @@ -696,12 +699,17 @@ const errorReasons: Record<ErrorCode, Protocol.Network.ErrorReason> = {
export type ActionResult = 'continue' | 'abort' | 'respond';

function headersArray(
headers: Record<string, string>
headers: Record<string, string | string[]>
): Array<{ name: string; value: string }> {
const result = [];
for (const name in headers) {
if (!Object.is(headers[name], undefined))
result.push({ name, value: headers[name] + '' });
const value = headers[name];

if (!Object.is(value, undefined)) {
const values = Array.isArray(value) ? value : [value];

result.push(...values.map((value) => ({ name, value: value + '' })));
}
}
return result;
}
Expand Down
27 changes: 27 additions & 0 deletions test/requestinterception.spec.ts
Expand Up @@ -710,6 +710,33 @@ describe('request interception', function () {
);
expect(response.url()).toBe(server.EMPTY_PAGE);
});
it('should allow mocking multiple headers with same key', async () => {
const { page, server } = getTestState();

await page.setRequestInterception(true);
page.on('request', (request) => {
request.respond({
status: 200,
headers: {
foo: 'bar',
arr: ['1', '2'],
'set-cookie': ['first=1', 'second=2'],
},
body: 'Hello world',
});
});
const response = await page.goto(server.EMPTY_PAGE);
const cookies = await page.cookies();
const firstCookie = cookies.find((cookie) => cookie.name === 'first');
const secondCookie = cookies.find((cookie) => cookie.name === 'second');
expect(response.status()).toBe(200);
expect(response.headers().foo).toBe('bar');
expect(response.headers().arr).toBe('1\n2');
// request.respond() will not trigger Network.responseReceivedExtraInfo
// fail to get 'set-cookie' header from response
expect(firstCookie?.value).toBe('1');
expect(secondCookie?.value).toBe('2');
});
it('should allow mocking binary responses', async () => {
const { page, server } = getTestState();

Expand Down

0 comments on commit c1dcd85

Please sign in to comment.