diff --git a/docs/src/api/class-route.md b/docs/src/api/class-route.md index 622dcc5d5a..39444b6c46 100644 --- a/docs/src/api/class-route.md +++ b/docs/src/api/class-route.md @@ -484,9 +484,9 @@ File path to respond with. The content type will be inferred from file extension is resolved relative to the current working directory. ### option: Route.fulfill.response -- `response` <[APIResponse]|[HARResponse]> +- `response` <[APIResponse]> -[APIResponse] or [HARResponse] to fulfill route's request with. Individual fields of the response (such as headers) can be overridden using fulfill options. +[APIResponse] to fulfill route's request with. Individual fields of the response (such as headers) can be overridden using fulfill options. ## method: Route.request - returns: <[Request]> diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 71c1baa73d..9489dc6ef2 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -21,7 +21,7 @@ import { Frame } from './frame'; import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types'; import fs from 'fs'; import { mime } from '../utilsBundle'; -import { isString, headersObjectToArray, headersArrayToObject } from '../utils'; +import { isString, headersObjectToArray } from '../utils'; import { ManualPromise } from '../utils/manualPromise'; import { Events } from './events'; import type { Page } from './page'; @@ -31,7 +31,6 @@ import type { HeadersArray, URLMatch } from '../common/types'; import { urlMatches } from '../common/netUtils'; import { MultiMap } from '../utils/multimap'; import { APIResponse } from './fetch'; -import type { HARResponse } from '../../types/har'; export type NetworkCookie = { name: string, @@ -292,7 +291,7 @@ export class Route extends ChannelOwner implements api.Ro this._reportHandled(true); } - async fulfill(options: { response?: api.APIResponse | HARResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) { + async fulfill(options: { response?: api.APIResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) { this._checkNotHandled(); await this._wrapApiCall(async () => { await this._innerFulfill(options); @@ -300,9 +299,9 @@ export class Route extends ChannelOwner implements api.Ro }); } - private async _innerFulfill(options: { response?: api.APIResponse | HARResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}): Promise { + private async _innerFulfill(options: { response?: api.APIResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}): Promise { let fetchResponseUid; - let { status: statusOption, headers: headersOption, body, contentType } = options; + let { status: statusOption, headers: headersOption, body } = options; if (options.response instanceof APIResponse) { statusOption ??= options.response.status(); @@ -313,16 +312,6 @@ export class Route extends ChannelOwner implements api.Ro else body = await options.response.body(); } - } else if (options.response) { - const harResponse = options.response as HARResponse; - statusOption ??= harResponse.status; - headersOption ??= headersArrayToObject(harResponse.headers, false); - if (body === undefined && options.path === undefined) { - body = harResponse.content.text; - contentType ??= harResponse.content.mimeType; - if (body !== undefined && harResponse.content.encoding === 'base64') - body = Buffer.from(body, 'base64'); - } } let isBase64 = false; @@ -344,8 +333,8 @@ export class Route extends ChannelOwner implements api.Ro const headers: Headers = {}; for (const header of Object.keys(headersOption || {})) headers[header.toLowerCase()] = String(headersOption![header]); - if (contentType) - headers['content-type'] = String(contentType); + if (options.contentType) + headers['content-type'] = String(options.contentType); else if (options.path) headers['content-type'] = mime.getType(options.path) || 'application/octet-stream'; if (length && !('content-length' in headers)) diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 1b10dbf212..958f635a18 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -24,7 +24,7 @@ import type { DispatcherScope } from './dispatcher'; import { Dispatcher } from './dispatcher'; import { yazl, yauzl } from '../../zipBundle'; import { ZipFile } from '../../utils/zipFile'; -import type { HAREntry, HARFile } from '../../../types/types'; +import type { HAREntry, HARFile } from '../../../types/har'; import type { HeadersArray } from '../types'; export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.LocalUtilsChannel> implements channels.LocalUtilsChannel { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index db1c798c25..2cc8f75343 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -22,8 +22,6 @@ import { Readable } from 'stream'; import { ReadStream } from 'fs'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from 'playwright-core/types/structs'; -export * from 'playwright-core/types/har'; - type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state?: 'visible'|'attached'; }; @@ -15457,10 +15455,10 @@ export interface Route { path?: string; /** - * [APIResponse] or [HARResponse] to fulfill route's request with. Individual fields of the response (such as headers) can - * be overridden using fulfill options. + * [APIResponse] to fulfill route's request with. Individual fields of the response (such as headers) can be overridden + * using fulfill options. */ - response?: APIResponse|HARResponse; + response?: APIResponse; /** * Response status code, defaults to `200`. diff --git a/tests/page/page-request-fulfill.spec.ts b/tests/page/page-request-fulfill.spec.ts index 31efbc1ef5..e584391d0a 100644 --- a/tests/page/page-request-fulfill.spec.ts +++ b/tests/page/page-request-fulfill.spec.ts @@ -17,7 +17,7 @@ import { test as base, expect } from './pageTest'; import fs from 'fs'; -import type { HARFile } from '@playwright/test'; +import type { HARFile, HARResponse } from 'playwright-core/types/har'; const it = base.extend<{ // We access test servers at 10.0.2.2 from inside the browser on Android, @@ -330,7 +330,14 @@ it('should fulfill with har response', async ({ page, isAndroid, asset }) => { const har = JSON.parse(await fs.promises.readFile(harPath, 'utf-8')) as HARFile; await page.route('**/*', async route => { const response = findResponse(har, route.request().url()); - await route.fulfill({ response }); + const headers = {}; + for (const { name, value } of response.headers) + headers[name] = value; + await route.fulfill({ + status: response.status, + headers, + body: Buffer.from(response.content.text || '', (response.content.encoding as 'base64' | undefined) || 'utf-8'), + }); }); await page.goto('http://no.playwright/'); // HAR contains a redirect for the script. @@ -339,23 +346,7 @@ it('should fulfill with har response', async ({ page, isAndroid, asset }) => { await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(0, 255, 255)'); }); -it('should override status when fulfill with response from har', async ({ page, isAndroid, asset }) => { - it.fixme(isAndroid); - - const harPath = asset('har-fulfill.har'); - const har = JSON.parse(await fs.promises.readFile(harPath, 'utf-8')) as HARFile; - await page.route('**/*', async route => { - const response = findResponse(har, route.request().url()); - await route.fulfill({ response, status: route.request().url().endsWith('.css') ? 404 : undefined }); - }); - await page.goto('http://no.playwright/'); - // Script should work. - expect(await page.evaluate('window.value')).toBe('foo'); - // 404 should fail the CSS and styles should not apply. - await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); -}); - -function findResponse(har: HARFile, url: string) { +function findResponse(har: HARFile, url: string): HARResponse { let entry; const originalUrl = url; while (url.trim()) { diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index 348432d986..cb26509c68 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -21,8 +21,6 @@ import { Readable } from 'stream'; import { ReadStream } from 'fs'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from 'playwright-core/types/structs'; -export * from 'playwright-core/types/har'; - type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state?: 'visible'|'attached'; };