Skip to content

Commit eb2ca70

Browse files
authored
api(route): allow fulfilling with a file path (#1301)
1 parent cf46f1b commit eb2ca70

File tree

7 files changed

+45
-10
lines changed

7 files changed

+45
-10
lines changed

docs/api.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ Browser-specific Coverage implementation, only available for Chromium atm. See [
10111011
- `'load'` - consider navigation to be finished when the `load` event is fired.
10121012
- `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
10131013
- `'networkidle0'` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
1014-
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
1014+
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
10151015
- `'nowait'` - do not wait.
10161016
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
10171017
- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully double clicked. The Promise will be rejected if there is no element matching `selector`.
@@ -3377,8 +3377,9 @@ page.on('requestfailed', request => {
33773377
- `response` <[Object]> Response that will fulfill this request
33783378
- `status` <[number]> Response status code, defaults to `200`.
33793379
- `headers` <[Object]> Optional response headers. Header values will be converted to a string.
3380-
- `contentType` <[string]> If set, equals to setting `Content-Type` response header
3381-
- `body` <[string]|[Buffer]> Optional response body
3380+
- `contentType` <[string]> If set, equals to setting `Content-Type` response header.
3381+
- `body` <[string]|[Buffer]> Optional response body.
3382+
- `path` <[string]> Optional file path to respond with. The content type will be inferred from file extension. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
33823383
- returns: <[Promise]>
33833384

33843385
Fulfills request with given response. To use this, request interception should
@@ -3397,8 +3398,11 @@ await page.route('**/*', request => {
33973398
});
33983399
```
33993400

3400-
> **NOTE** Mocking responses for dataURL requests is not supported.
3401-
> Calling `request.fulfill` for a dataURL request is a noop.
3401+
An example of serving static file:
3402+
3403+
```js
3404+
await page.route('**/xhr_endpoint', request => request.fulfill({ path: 'mock_data.json' }));
3405+
```
34023406

34033407
#### request.headers()
34043408
- returns: <[Object]> An object with HTTP headers associated with the request. All header names are lower-case.

src/chromium/crNetworkManager.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class InterceptableRequest implements network.RequestDelegate {
267267
});
268268
}
269269

270-
async fulfill(response: { status: number; headers: network.Headers; contentType: string; body: (string | platform.BufferType); }) {
270+
async fulfill(response: network.FulfillResponse) {
271271
const responseBody = response.body && helper.isString(response.body) ? platform.Buffer.from(response.body) : (response.body || null);
272272

273273
const responseHeaders: { [s: string]: string; } = {};

src/firefox/ffNetworkManager.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class InterceptableRequest implements network.RequestDelegate {
174174
});
175175
}
176176

177-
async fulfill(response: { status: number; headers: network.Headers; contentType: string; body: (string | platform.BufferType); }) {
177+
async fulfill(response: network.FulfillResponse) {
178178
const responseBody = response.body && helper.isString(response.body) ? platform.Buffer.from(response.body) : (response.body || null);
179179

180180
const responseHeaders: { [s: string]: string; } = {};

src/network.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,18 @@ export class Request {
202202
await this._delegate.abort(errorCode);
203203
}
204204

205-
async fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }) { // Mocking responses for dataURL requests is not currently supported.
205+
async fulfill(response: FulfillResponse & { path?: string }) {
206206
assert(this._delegate, 'Request Interception is not enabled!');
207207
assert(!this._interceptionHandled, 'Request is already handled!');
208208
this._interceptionHandled = true;
209+
if (response.path) {
210+
response = {
211+
status: response.status,
212+
headers: response.headers,
213+
contentType: platform.getMimeType(response.path),
214+
body: await platform.readFileBuffer(response.path)
215+
};
216+
}
209217
await this._delegate.fulfill(response);
210218
}
211219

@@ -304,9 +312,16 @@ export class Response {
304312
}
305313
}
306314

315+
export type FulfillResponse = {
316+
status?: number,
317+
headers?: Headers,
318+
contentType?: string,
319+
body?: string | platform.BufferType,
320+
};
321+
307322
export interface RequestDelegate {
308323
abort(errorCode: string): Promise<void>;
309-
fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }): Promise<void>;
324+
fulfill(response: FulfillResponse): Promise<void>;
310325
continue(overrides: { method?: string; headers?: Headers; postData?: string; }): Promise<void>;
311326
}
312327

src/platform.ts

+5
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ export async function readFileAsync(file: string, encoding: string): Promise<str
187187
return await promisify(nodeFS.readFile)(file, encoding);
188188
}
189189

190+
export async function readFileBuffer(file: string): Promise<BufferType> {
191+
assertFileAccess();
192+
return await promisify(nodeFS.readFile)(file);
193+
}
194+
190195
export async function writeFileAsync(file: string, data: any) {
191196
assertFileAccess();
192197
return await promisify(nodeFS.writeFile)(file, data);

src/webkit/wkInterceptableRequest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class WKInterceptableRequest implements network.RequestDelegate {
6565
});
6666
}
6767

68-
async fulfill(response: { status: number; headers: network.Headers; contentType: string; body: (string | platform.BufferType); }) {
68+
async fulfill(response: network.FulfillResponse) {
6969
await this._interceptedPromise;
7070

7171
const base64Encoded = !!response.body && !helper.isString(response.body);

test/interception.spec.js

+11
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,17 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
468468
const img = await page.$('img');
469469
expect(await img.screenshot()).toBeGolden('mock-binary-response.png');
470470
});
471+
it('should work with file path', async({page, server}) => {
472+
await page.route('**/*', request => request.fulfill({ contentType: 'shouldBeIgnored', path: path.join(__dirname, 'assets', 'pptr.png') }));
473+
await page.evaluate(PREFIX => {
474+
const img = document.createElement('img');
475+
img.src = PREFIX + '/does-not-exist.png';
476+
document.body.appendChild(img);
477+
return new Promise(fulfill => img.onload = fulfill);
478+
}, server.PREFIX);
479+
const img = await page.$('img');
480+
expect(await img.screenshot()).toBeGolden('mock-binary-response.png');
481+
});
471482
it('should stringify intercepted request response headers', async({page, server}) => {
472483
await page.route('**/*', request => {
473484
request.fulfill({

0 commit comments

Comments
 (0)