diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts index 163ff3ec0c..e3b541ee9a 100644 --- a/packages/playwright-core/src/client/channelOwner.ts +++ b/packages/playwright-core/src/client/channelOwner.ts @@ -141,6 +141,14 @@ export abstract class ChannelOwner this._platform.isUnderTest(), + }; + } + private _createChannel(base: Object): T { const channel = new Proxy(base, { get: (obj: any, prop: string | symbol) => { @@ -149,7 +157,7 @@ export abstract class ChannelOwner { return await this._wrapApiCall(async apiZone => { - const validatedParams = validator(params, '', { tChannelImpl: tChannelImplToWire, binary: this._connection.rawBuffers() ? 'buffer' : 'toBase64' }); + const validatedParams = validator(params, '', this._validatorToWireContext()); if (!apiZone.isInternal && !apiZone.reported) { // Reporting/tracing/logging this api call for the first time. apiZone.params = params; diff --git a/packages/playwright-core/src/client/clientBundle.ts b/packages/playwright-core/src/client/clientBundle.ts index 28bfcf2af1..99df031213 100644 --- a/packages/playwright-core/src/client/clientBundle.ts +++ b/packages/playwright-core/src/client/clientBundle.ts @@ -16,12 +16,10 @@ import { Connection } from './connection'; import { setPlatformForSelectors } from './selectors'; -import { setIsUnderTestForValidator } from '../protocol/validatorPrimitives'; import type { Platform } from './platform'; export function createConnectionFactory(platform: Platform): () => Connection { setPlatformForSelectors(platform); - setIsUnderTestForValidator(() => platform.isUnderTest()); return () => new Connection(platform); } diff --git a/packages/playwright-core/src/client/connection.ts b/packages/playwright-core/src/client/connection.ts index b123728a9b..1e83eb2af7 100644 --- a/packages/playwright-core/src/client/connection.ts +++ b/packages/playwright-core/src/client/connection.ts @@ -148,6 +148,14 @@ export class Connection extends EventEmitter { return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, apiName, type, method })); } + private _validatorFromWireContext(): ValidatorContext { + return { + tChannelImpl: this._tChannelImplFromWire.bind(this), + binary: this._rawBuffers ? 'buffer' : 'fromBase64', + isUnderTest: () => this._platform.isUnderTest(), + }; + } + dispatch(message: object) { if (this._closedError) return; @@ -166,7 +174,7 @@ export class Connection extends EventEmitter { callback.reject(parsedError); } else { const validator = findValidator(callback.type, callback.method, 'Result'); - callback.resolve(validator(result, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._rawBuffers ? 'buffer' : 'fromBase64' })); + callback.resolve(validator(result, '', this._validatorFromWireContext())); } return; } @@ -196,7 +204,7 @@ export class Connection extends EventEmitter { } const validator = findValidator(object._type, method, 'Event'); - (object._channel as any).emit(method, validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._rawBuffers ? 'buffer' : 'fromBase64' })); + (object._channel as any).emit(method, validator(params, '', this._validatorFromWireContext())); } close(cause?: string) { @@ -227,7 +235,7 @@ export class Connection extends EventEmitter { throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`); let result: ChannelOwner; const validator = findValidator(type, '', 'Initializer'); - initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._rawBuffers ? 'buffer' : 'fromBase64' }); + initializer = validator(initializer, '', this._validatorFromWireContext()); switch (type) { case 'Android': result = new Android(parent, type, guid, initializer); diff --git a/packages/playwright-core/src/protocol/validatorPrimitives.ts b/packages/playwright-core/src/protocol/validatorPrimitives.ts index 2c1a097da5..eadd2e014e 100644 --- a/packages/playwright-core/src/protocol/validatorPrimitives.ts +++ b/packages/playwright-core/src/protocol/validatorPrimitives.ts @@ -14,17 +14,12 @@ * limitations under the License. */ -let isUnderTest = () => false; - -export function setIsUnderTestForValidator(getter: () => boolean) { - isUnderTest = getter; -} - export class ValidationError extends Error {} export type Validator = (arg: any, path: string, context: ValidatorContext) => any; export type ValidatorContext = { - tChannelImpl: (names: '*' | string[], arg: any, path: string, context: ValidatorContext) => any, - binary: 'toBase64' | 'fromBase64' | 'buffer', + tChannelImpl: (names: '*' | string[], arg: any, path: string, context: ValidatorContext) => any; + binary: 'toBase64' | 'fromBase64' | 'buffer'; + isUnderTest: () => boolean; }; export const scheme: { [key: string]: Validator } = {}; @@ -117,7 +112,7 @@ export const tObject = (s: { [key: string]: Validator }): Validator => { if (!Object.is(value, undefined)) result[key] = value; } - if (isUnderTest()) { + if (context.isUnderTest()) { for (const [key, value] of Object.entries(arg)) { if (key.startsWith('__testHook')) result[key] = value; diff --git a/packages/playwright-core/src/server/dispatchers/dispatcher.ts b/packages/playwright-core/src/server/dispatchers/dispatcher.ts index c6eb6f7868..c96d18339d 100644 --- a/packages/playwright-core/src/server/dispatchers/dispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/dispatcher.ts @@ -200,13 +200,13 @@ export class DispatcherConnection { sendEvent(dispatcher: DispatcherScope, event: string, params: any) { const validator = findValidator(dispatcher._type, event, 'Event'); - params = validator(params, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' }); + params = validator(params, '', this._validatorToWireContext()); this.onmessage({ guid: dispatcher._guid, method: event, params }); } sendCreate(parent: DispatcherScope, type: string, guid: string, initializer: any) { const validator = findValidator(type, '', 'Initializer'); - initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' }); + initializer = validator(initializer, '', this._validatorToWireContext()); this.onmessage({ guid: parent._guid, method: '__create__', params: { type, initializer, guid } }); } @@ -218,6 +218,22 @@ export class DispatcherConnection { this.onmessage({ guid: dispatcher._guid, method: '__dispose__', params: { reason } }); } + private _validatorToWireContext(): ValidatorContext { + return { + tChannelImpl: this._tChannelImplToWire.bind(this), + binary: this._isLocal ? 'buffer' : 'toBase64', + isUnderTest, + }; + } + + private _validatorFromWireContext(): ValidatorContext { + return { + tChannelImpl: this._tChannelImplFromWire.bind(this), + binary: this._isLocal ? 'buffer' : 'fromBase64', + isUnderTest, + }; + } + private _tChannelImplFromWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext): any { if (arg && typeof arg === 'object' && typeof arg.guid === 'string') { const guid = arg.guid; @@ -279,8 +295,9 @@ export class DispatcherConnection { let validMetadata: channels.Metadata; try { const validator = findValidator(dispatcher._type, method, 'Params'); - validParams = validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._isLocal ? 'buffer' : 'fromBase64' }); - validMetadata = metadataValidator(metadata, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._isLocal ? 'buffer' : 'fromBase64' }); + const validatorContext = this._validatorFromWireContext(); + validParams = validator(params, '', validatorContext); + validMetadata = metadataValidator(metadata, '', validatorContext); if (typeof (dispatcher as any)[method] !== 'function') throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`); } catch (e) { @@ -338,7 +355,7 @@ export class DispatcherConnection { try { const result = await dispatcher._handleCommand(callMetadata, method, validParams); const validator = findValidator(dispatcher._type, method, 'Result'); - response.result = validator(result, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' }); + response.result = validator(result, '', this._validatorToWireContext()); callMetadata.result = result; } catch (e) { if (isTargetClosedError(e) && sdkObject) { diff --git a/packages/playwright-core/src/server/socksInterceptor.ts b/packages/playwright-core/src/server/socksInterceptor.ts index 044c1d826a..c29981204f 100644 --- a/packages/playwright-core/src/server/socksInterceptor.ts +++ b/packages/playwright-core/src/server/socksInterceptor.ts @@ -18,6 +18,7 @@ import EventEmitter from 'events'; import * as socks from './utils/socksProxy'; import { ValidationError, findValidator } from '../protocol/validator'; +import { isUnderTest } from './utils/debug'; import type { WebSocketTransport } from './transport'; import type { ValidatorContext } from '../protocol/validator'; @@ -42,7 +43,7 @@ export class SocksInterceptor { const id = --lastId; this._ids.add(id); const validator = findValidator('SocksSupport', prop, 'Params'); - params = validator(params, '', { tChannelImpl: tChannelForSocks, binary: 'toBase64' }); + params = validator(params, '', { tChannelImpl: tChannelForSocks, binary: 'toBase64', isUnderTest }); transport.send({ id, guid: this._socksSupportObjectGuid, method: prop, params, metadata: { stack: [], apiName: '', internal: true } } as any); } catch (e) { } @@ -74,7 +75,7 @@ export class SocksInterceptor { } if (this._socksSupportObjectGuid && message.guid === this._socksSupportObjectGuid) { const validator = findValidator('SocksSupport', message.method, 'Event'); - const params = validator(message.params, '', { tChannelImpl: tChannelForSocks, binary: 'fromBase64' }); + const params = validator(message.params, '', { tChannelImpl: tChannelForSocks, binary: 'fromBase64', isUnderTest }); this._channel.emit(message.method, params); return true; }