refactor
This commit is contained in:
parent
5a22475ea8
commit
6429c7672e
|
|
@ -148,15 +148,15 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
if (validator) {
|
if (validator) {
|
||||||
return async (params: any) => {
|
return async (params: any) => {
|
||||||
return await this._wrapApiCall(async apiZone => {
|
return await this._wrapApiCall(async apiZone => {
|
||||||
const { apiName, frames, csi, callCookie, stepId } = apiZone.reported ? { apiName: undefined, csi: undefined, callCookie: undefined, frames: [], stepId: undefined } : apiZone;
|
const validatedParams = validator(params, '', { tChannelImpl: tChannelImplToWire, binary: this._connection.rawBuffers() ? 'buffer' : 'toBase64' });
|
||||||
|
if (!apiZone.isInternal && !apiZone.reported) {
|
||||||
|
apiZone.params = params;
|
||||||
apiZone.reported = true;
|
apiZone.reported = true;
|
||||||
let currentStepId = stepId;
|
logApiCall(this._logger, `=> ${apiZone.apiName} started`);
|
||||||
if (csi && apiName) {
|
apiZone.csi?.onApiCallBegin(apiZone);
|
||||||
const out: { stepId?: string } = {};
|
return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone.apiName, apiZone.frames, apiZone.stepId);
|
||||||
csi.onApiCallBegin(apiName, params, frames, callCookie, out);
|
|
||||||
currentStepId = out.stepId;
|
|
||||||
}
|
}
|
||||||
return await this._connection.sendMessageToServer(this, prop, validator(params, '', { tChannelImpl: tChannelImplToWire, binary: this._connection.rawBuffers() ? 'buffer' : 'toBase64' }), apiName, frames, currentStepId);
|
return await this._connection.sendMessageToServer(this, prop, validatedParams, undefined, [], undefined);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -170,9 +170,9 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
|
|
||||||
async _wrapApiCall<R>(func: (apiZone: ApiZone) => Promise<R>, isInternal?: boolean): Promise<R> {
|
async _wrapApiCall<R>(func: (apiZone: ApiZone) => Promise<R>, isInternal?: boolean): Promise<R> {
|
||||||
const logger = this._logger;
|
const logger = this._logger;
|
||||||
const apiZone = zones.zoneData<ApiZone>('apiZone');
|
const existingApiZone = zones.zoneData<ApiZone>('apiZone');
|
||||||
if (apiZone)
|
if (existingApiZone)
|
||||||
return await func(apiZone);
|
return await func(existingApiZone);
|
||||||
|
|
||||||
const stackTrace = captureLibraryStackTrace();
|
const stackTrace = captureLibraryStackTrace();
|
||||||
let apiName: string | undefined = stackTrace.apiName;
|
let apiName: string | undefined = stackTrace.apiName;
|
||||||
|
|
@ -180,26 +180,23 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
|
|
||||||
if (isInternal === undefined)
|
if (isInternal === undefined)
|
||||||
isInternal = this._isInternalType;
|
isInternal = this._isInternalType;
|
||||||
if (isInternal)
|
|
||||||
apiName = undefined;
|
|
||||||
|
|
||||||
// Enclosing zone could have provided the apiName and wallTime.
|
// Enclosing zone could have provided the apiName and wallTime.
|
||||||
const expectZone = zones.zoneData<ExpectZone>('expectZone');
|
const expectZone = zones.zoneData<ExpectZone>('expectZone');
|
||||||
const stepId = expectZone?.stepId;
|
const stepId = expectZone?.stepId;
|
||||||
if (!isInternal && expectZone)
|
if (expectZone)
|
||||||
apiName = expectZone.title;
|
apiName = expectZone.title;
|
||||||
|
|
||||||
// If we are coming from the expectZone, there is no need to generate a new
|
// If we are coming from the expectZone, there is no need to generate a new
|
||||||
// step for the API call, since it will be generated by the expect itself.
|
// step for the API call, since it will be generated by the expect itself.
|
||||||
const csi = isInternal || expectZone ? undefined : this._instrumentation;
|
const csi = isInternal || expectZone ? undefined : this._instrumentation;
|
||||||
const callCookie: any = {};
|
const apiZone: ApiZone = { apiName, frames, isInternal, reported: false, csi, userData: undefined, stepId };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logApiCall(logger, `=> ${apiName} started`, isInternal);
|
|
||||||
const apiZone: ApiZone = { apiName, frames, isInternal, reported: false, csi, callCookie, stepId };
|
|
||||||
const result = await zones.run('apiZone', apiZone, async () => await func(apiZone));
|
const result = await zones.run('apiZone', apiZone, async () => await func(apiZone));
|
||||||
csi?.onApiCallEnd(callCookie);
|
csi?.onApiCallEnd(apiZone);
|
||||||
logApiCall(logger, `<= ${apiName} succeeded`, isInternal);
|
if (!isInternal)
|
||||||
|
logApiCall(logger, `<= ${apiName} succeeded`);
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const innerError = ((process.env.PWDEBUGIMPL || isUnderTest()) && e.stack) ? '\n<inner error>\n' + e.stack : '';
|
const innerError = ((process.env.PWDEBUGIMPL || isUnderTest()) && e.stack) ? '\n<inner error>\n' + e.stack : '';
|
||||||
|
|
@ -210,8 +207,9 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
e.stack = e.message + stackFrames;
|
e.stack = e.message + stackFrames;
|
||||||
else
|
else
|
||||||
e.stack = '';
|
e.stack = '';
|
||||||
csi?.onApiCallEnd(callCookie, e);
|
csi?.onApiCallEnd(apiZone);
|
||||||
logApiCall(logger, `<= ${apiName} failed`, isInternal);
|
if (!isInternal)
|
||||||
|
logApiCall(logger, `<= ${apiName} failed`);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,9 +230,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logApiCall(logger: Logger | undefined, message: string, isNested: boolean) {
|
function logApiCall(logger: Logger | undefined, message: string) {
|
||||||
if (isNested)
|
|
||||||
return;
|
|
||||||
if (logger && logger.isEnabled('api', 'info'))
|
if (logger && logger.isEnabled('api', 'info'))
|
||||||
logger.log('api', 'info', message, [], { color: 'cyan' });
|
logger.log('api', 'info', message, [], { color: 'cyan' });
|
||||||
debugLogger.log('api', message);
|
debugLogger.log('api', message);
|
||||||
|
|
@ -247,11 +243,13 @@ function tChannelImplToWire(names: '*' | string[], arg: any, path: string, conte
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiZone = {
|
type ApiZone = {
|
||||||
apiName: string | undefined;
|
apiName: string;
|
||||||
|
params?: Record<string, any>;
|
||||||
frames: channels.StackFrame[];
|
frames: channels.StackFrame[];
|
||||||
isInternal: boolean;
|
isInternal: boolean;
|
||||||
reported: boolean;
|
reported: boolean;
|
||||||
csi: ClientInstrumentation | undefined;
|
csi: ClientInstrumentation | undefined;
|
||||||
callCookie: any;
|
userData: any;
|
||||||
stepId?: string;
|
stepId?: string;
|
||||||
|
error?: Error;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,22 @@ import type { StackFrame } from '@protocol/channels';
|
||||||
import type { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
import type { APIRequestContext } from './fetch';
|
import type { APIRequestContext } from './fetch';
|
||||||
|
|
||||||
|
// Instrumentation can mutate the data, for example change apiName or stepId.
|
||||||
|
export interface ApiCallData {
|
||||||
|
apiName: string;
|
||||||
|
params?: Record<string, any>;
|
||||||
|
frames: StackFrame[];
|
||||||
|
userData: any;
|
||||||
|
stepId?: string;
|
||||||
|
error?: Error;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ClientInstrumentation {
|
export interface ClientInstrumentation {
|
||||||
addListener(listener: ClientInstrumentationListener): void;
|
addListener(listener: ClientInstrumentationListener): void;
|
||||||
removeListener(listener: ClientInstrumentationListener): void;
|
removeListener(listener: ClientInstrumentationListener): void;
|
||||||
removeAllListeners(): void;
|
removeAllListeners(): void;
|
||||||
onApiCallBegin(apiCall: string, params: Record<string, any>, frames: StackFrame[], userData: any, out: { stepId?: string }): void;
|
onApiCallBegin(apiCall: ApiCallData): void;
|
||||||
onApiCallEnd(userData: any, error?: Error): void;
|
onApiCallEnd(apiCal: ApiCallData): void;
|
||||||
onWillPause(options: { keepTestTimeout: boolean }): void;
|
onWillPause(options: { keepTestTimeout: boolean }): void;
|
||||||
|
|
||||||
runAfterCreateBrowserContext(context: BrowserContext): Promise<void>;
|
runAfterCreateBrowserContext(context: BrowserContext): Promise<void>;
|
||||||
|
|
@ -33,8 +43,8 @@ export interface ClientInstrumentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClientInstrumentationListener {
|
export interface ClientInstrumentationListener {
|
||||||
onApiCallBegin?(apiName: string, params: Record<string, any>, frames: StackFrame[], userData: any, out: { stepId?: string }): void;
|
onApiCallBegin?(apiCall: ApiCallData): void;
|
||||||
onApiCallEnd?(userData: any, error?: Error): void;
|
onApiCallEnd?(apiCall: ApiCallData): void;
|
||||||
onWillPause?(options: { keepTestTimeout: boolean }): void;
|
onWillPause?(options: { keepTestTimeout: boolean }): void;
|
||||||
|
|
||||||
runAfterCreateBrowserContext?(context: BrowserContext): Promise<void>;
|
runAfterCreateBrowserContext?(context: BrowserContext): Promise<void>;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWor
|
||||||
import type { TestInfoImpl, TestStepInternal } from './worker/testInfo';
|
import type { TestInfoImpl, TestStepInternal } from './worker/testInfo';
|
||||||
import { rootTestType } from './common/testType';
|
import { rootTestType } from './common/testType';
|
||||||
import type { ContextReuseMode } from './common/config';
|
import type { ContextReuseMode } from './common/config';
|
||||||
import type { ClientInstrumentation, ClientInstrumentationListener } from '../../playwright-core/src/client/clientInstrumentation';
|
import type { ApiCallData, ClientInstrumentation, ClientInstrumentationListener } from '../../playwright-core/src/client/clientInstrumentation';
|
||||||
import { currentTestInfo } from './common/globals';
|
import { currentTestInfo } from './common/globals';
|
||||||
export { expect } from './matchers/expect';
|
export { expect } from './matchers/expect';
|
||||||
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
||||||
|
|
@ -258,34 +258,33 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
|
|
||||||
const tracingGroupSteps: TestStepInternal[] = [];
|
const tracingGroupSteps: TestStepInternal[] = [];
|
||||||
const csiListener: ClientInstrumentationListener = {
|
const csiListener: ClientInstrumentationListener = {
|
||||||
onApiCallBegin: (apiName: string, params: Record<string, any>, frames: StackFrame[], userData: any, out: { stepId?: string }) => {
|
onApiCallBegin: (data: ApiCallData) => {
|
||||||
userData.apiName = apiName;
|
|
||||||
const testInfo = currentTestInfo();
|
const testInfo = currentTestInfo();
|
||||||
if (!testInfo || apiName.includes('setTestIdAttribute') || apiName === 'tracing.groupEnd')
|
if (!testInfo || data.apiName.includes('setTestIdAttribute') || data.apiName === 'tracing.groupEnd')
|
||||||
return;
|
return;
|
||||||
const step = testInfo._addStep({
|
const step = testInfo._addStep({
|
||||||
location: frames[0] as any,
|
location: data.frames[0],
|
||||||
category: 'pw:api',
|
category: 'pw:api',
|
||||||
title: renderApiCall(apiName, params),
|
title: renderApiCall(data.apiName, data.params),
|
||||||
apiName,
|
apiName: data.apiName,
|
||||||
params,
|
params: data.params,
|
||||||
}, tracingGroupSteps[tracingGroupSteps.length - 1]);
|
}, tracingGroupSteps[tracingGroupSteps.length - 1]);
|
||||||
userData.step = step;
|
data.userData = step;
|
||||||
out.stepId = step.stepId;
|
data.stepId = step.stepId;
|
||||||
if (apiName === 'tracing.group')
|
if (data.apiName === 'tracing.group')
|
||||||
tracingGroupSteps.push(step);
|
tracingGroupSteps.push(step);
|
||||||
},
|
},
|
||||||
onApiCallEnd: (userData: any, error?: Error) => {
|
onApiCallEnd: (data: ApiCallData) => {
|
||||||
// "tracing.group" step will end later, when "tracing.groupEnd" finishes.
|
// "tracing.group" step will end later, when "tracing.groupEnd" finishes.
|
||||||
if (userData.apiName === 'tracing.group')
|
if (data.apiName === 'tracing.group')
|
||||||
return;
|
return;
|
||||||
if (userData.apiName === 'tracing.groupEnd') {
|
if (data.apiName === 'tracing.groupEnd') {
|
||||||
const step = tracingGroupSteps.pop();
|
const step = tracingGroupSteps.pop();
|
||||||
step?.complete({ error });
|
step?.complete({ error: data.error });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const step = userData.step;
|
const step = data.userData;
|
||||||
step?.complete({ error });
|
step?.complete({ error: data.error });
|
||||||
},
|
},
|
||||||
onWillPause: ({ keepTestTimeout }) => {
|
onWillPause: ({ keepTestTimeout }) => {
|
||||||
if (!keepTestTimeout)
|
if (!keepTestTimeout)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue