chore: move zones into platform (#34786)
This commit is contained in:
parent
9ecf2f69ba
commit
90ec838318
|
@ -338,7 +338,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
}
|
||||
|
||||
async route(url: URLMatch, handler: network.RouteHandlerCallback, options: { times?: number } = {}): Promise<void> {
|
||||
this._routes.unshift(new network.RouteHandler(this._options.baseURL, url, handler, options.times));
|
||||
this._routes.unshift(new network.RouteHandler(this._platform, this._options.baseURL, url, handler, options.times));
|
||||
await this._updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import { EventEmitter } from './eventEmitter';
|
|||
import { ValidationError, maybeFindValidator } from '../protocol/validator';
|
||||
import { isUnderTest } from '../utils/isomorphic/debug';
|
||||
import { captureLibraryStackTrace, stringifyStackFrames } from '../utils/isomorphic/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
|
||||
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||
import type { Connection } from './connection';
|
||||
|
@ -176,7 +175,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
|||
|
||||
async _wrapApiCall<R>(func: (apiZone: ApiZone) => Promise<R>, isInternal?: boolean): Promise<R> {
|
||||
const logger = this._logger;
|
||||
const existingApiZone = zones.zoneData<ApiZone>('apiZone');
|
||||
const existingApiZone = this._platform.zones.current().data<ApiZone>();
|
||||
if (existingApiZone)
|
||||
return await func(existingApiZone);
|
||||
|
||||
|
@ -186,7 +185,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
|||
const apiZone: ApiZone = { apiName: stackTrace.apiName, frames: stackTrace.frames, isInternal, reported: false, userData: undefined, stepId: undefined };
|
||||
|
||||
try {
|
||||
const result = await zones.run('apiZone', apiZone, async () => await func(apiZone));
|
||||
const result = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone));
|
||||
if (!isInternal) {
|
||||
logApiCall(this._platform, logger, `<= ${apiZone.apiName} succeeded`);
|
||||
this._instrumentation.onApiCallEnd(apiZone);
|
||||
|
|
|
@ -43,7 +43,6 @@ import { Worker } from './worker';
|
|||
import { WritableStream } from './writableStream';
|
||||
import { ValidationError, findValidator } from '../protocol/validator';
|
||||
import { formatCallLog, rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
|
||||
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||
import type { HeadersArray } from './types';
|
||||
|
@ -148,7 +147,7 @@ export class Connection extends EventEmitter {
|
|||
this._localUtils?.addStackToTracingNoReply({ callData: { stack: frames, id } }).catch(() => {});
|
||||
// We need to exit zones before calling into the server, otherwise
|
||||
// when we receive events from the server, we would be in an API zone.
|
||||
zones.empty().run(() => this.onmessage({ ...message, metadata }));
|
||||
this.platform.zones.empty.run(() => this.onmessage({ ...message, metadata }));
|
||||
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, apiName, type, method }));
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ import { LongStandingScope, ManualPromise } from '../utils/isomorphic/manualProm
|
|||
import { MultiMap } from '../utils/isomorphic/multimap';
|
||||
import { isRegExp, isString } from '../utils/isomorphic/rtti';
|
||||
import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
import { mime } from '../utilsBundle';
|
||||
|
||||
import type { BrowserContext } from './browserContext';
|
||||
|
@ -40,8 +39,8 @@ import type { Serializable } from '../../types/structs';
|
|||
import type * as api from '../../types/types';
|
||||
import type { HeadersArray } from '../common/types';
|
||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||
import type { Zone } from '../utils/zones';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import type { Platform, Zone } from '../common/platform';
|
||||
|
||||
export type NetworkCookie = {
|
||||
name: string,
|
||||
|
@ -821,14 +820,14 @@ export class RouteHandler {
|
|||
readonly handler: RouteHandlerCallback;
|
||||
private _ignoreException: boolean = false;
|
||||
private _activeInvocations: Set<{ complete: Promise<void>, route: Route }> = new Set();
|
||||
private _svedZone: Zone;
|
||||
private _savedZone: Zone;
|
||||
|
||||
constructor(baseURL: string | undefined, url: URLMatch, handler: RouteHandlerCallback, times: number = Number.MAX_SAFE_INTEGER) {
|
||||
constructor(platform: Platform, baseURL: string | undefined, url: URLMatch, handler: RouteHandlerCallback, times: number = Number.MAX_SAFE_INTEGER) {
|
||||
this._baseURL = baseURL;
|
||||
this._times = times;
|
||||
this.url = url;
|
||||
this.handler = handler;
|
||||
this._svedZone = zones.current().without('apiZone');
|
||||
this._savedZone = platform.zones.current().pop();
|
||||
}
|
||||
|
||||
static prepareInterceptionPatterns(handlers: RouteHandler[]) {
|
||||
|
@ -852,7 +851,7 @@ export class RouteHandler {
|
|||
}
|
||||
|
||||
public async handle(route: Route): Promise<boolean> {
|
||||
return await this._svedZone.run(async () => this._handleImpl(route));
|
||||
return await this._savedZone.run(async () => this._handleImpl(route));
|
||||
}
|
||||
|
||||
private async _handleImpl(route: Route): Promise<boolean> {
|
||||
|
|
|
@ -520,7 +520,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
}
|
||||
|
||||
async route(url: URLMatch, handler: RouteHandlerCallback, options: { times?: number } = {}): Promise<void> {
|
||||
this._routes.unshift(new RouteHandler(this._browserContext._options.baseURL, url, handler, options.times));
|
||||
this._routes.unshift(new RouteHandler(this._platform, this._browserContext._options.baseURL, url, handler, options.times));
|
||||
await this._updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
import { TimeoutError } from './errors';
|
||||
import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
|
||||
import type { ChannelOwner } from './channelOwner';
|
||||
import type { Zone } from '../utils/zones';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import type { EventEmitter } from 'events';
|
||||
import type { Zone } from '../common/platform';
|
||||
|
||||
export class Waiter {
|
||||
private _dispose: (() => void)[];
|
||||
|
@ -36,7 +35,7 @@ export class Waiter {
|
|||
constructor(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) {
|
||||
this._waitId = channelOwner._platform.createGuid();
|
||||
this._channelOwner = channelOwner;
|
||||
this._savedZone = zones.current().without('apiZone');
|
||||
this._savedZone = channelOwner._platform.zones.current().pop();
|
||||
|
||||
this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {});
|
||||
this._dispose = [
|
||||
|
|
|
@ -22,6 +22,19 @@ import { webColors, noColors } from '../utils/isomorphic/colors';
|
|||
|
||||
import type { Colors } from '../utils/isomorphic/colors';
|
||||
|
||||
export type Zone = {
|
||||
push(data: unknown): Zone;
|
||||
pop(): Zone;
|
||||
run<R>(func: () => R): R;
|
||||
data<T>(): T | undefined;
|
||||
};
|
||||
|
||||
const noopZone: Zone = {
|
||||
push: () => noopZone,
|
||||
pop: () => noopZone,
|
||||
run: func => func(),
|
||||
data: () => undefined,
|
||||
};
|
||||
|
||||
export type Platform = {
|
||||
calculateSha1(text: string): Promise<string>;
|
||||
|
@ -34,6 +47,7 @@ export type Platform = {
|
|||
path: () => typeof path;
|
||||
pathSeparator: string;
|
||||
ws?: (url: string) => WebSocket;
|
||||
zones: { empty: Zone, current: () => Zone; };
|
||||
};
|
||||
|
||||
export const webPlatform: Platform = {
|
||||
|
@ -69,6 +83,8 @@ export const webPlatform: Platform = {
|
|||
pathSeparator: '/',
|
||||
|
||||
ws: (url: string) => new WebSocket(url),
|
||||
|
||||
zones: { empty: noopZone, current: () => noopZone },
|
||||
};
|
||||
|
||||
export const emptyPlatform: Platform = {
|
||||
|
@ -98,5 +114,7 @@ export const emptyPlatform: Platform = {
|
|||
throw new Error('Function not implemented.');
|
||||
},
|
||||
|
||||
pathSeparator: '/'
|
||||
pathSeparator: '/',
|
||||
|
||||
zones: { empty: noopZone, current: () => noopZone },
|
||||
};
|
||||
|
|
|
@ -20,8 +20,39 @@ import * as path from 'path';
|
|||
import * as util from 'util';
|
||||
|
||||
import { colors } from '../../utilsBundle';
|
||||
import { Platform } from '../../common/platform';
|
||||
import { debugLogger } from './debugLogger';
|
||||
import { currentZone, emptyZone } from './zones';
|
||||
|
||||
import type { Platform, Zone } from '../../common/platform';
|
||||
import type { Zone as ZoneImpl } from './zones';
|
||||
|
||||
class NodeZone implements Zone {
|
||||
private _zone: ZoneImpl;
|
||||
|
||||
constructor(zone: ZoneImpl) {
|
||||
this._zone = zone;
|
||||
}
|
||||
|
||||
push<T>(data: T) {
|
||||
return new NodeZone(this._zone.with('apiZone', data));
|
||||
}
|
||||
|
||||
pop() {
|
||||
return new NodeZone(this._zone.without('apiZone'));
|
||||
}
|
||||
|
||||
run<R>(func: () => R): R {
|
||||
return this._zone.run(func);
|
||||
}
|
||||
|
||||
runIgnoreCurrent<R>(func: () => R): R {
|
||||
return emptyZone.run(func);
|
||||
}
|
||||
|
||||
data<T>(): T | undefined {
|
||||
return this._zone.data('apiZone');
|
||||
}
|
||||
}
|
||||
|
||||
export const nodePlatform: Platform = {
|
||||
calculateSha1: (text: string) => {
|
||||
|
@ -48,5 +79,10 @@ export const nodePlatform: Platform = {
|
|||
|
||||
path: () => path,
|
||||
|
||||
pathSeparator: path.sep
|
||||
pathSeparator: path.sep,
|
||||
|
||||
zones: {
|
||||
current: () => new NodeZone(currentZone()),
|
||||
empty: new NodeZone(emptyZone),
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,36 +18,13 @@ import { AsyncLocalStorage } from 'async_hooks';
|
|||
|
||||
export type ZoneType = 'apiZone' | 'stepZone';
|
||||
|
||||
class ZoneManager {
|
||||
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone | undefined>();
|
||||
private readonly _emptyZone = Zone.createEmpty(this._asyncLocalStorage);
|
||||
|
||||
run<T, R>(type: ZoneType, data: T, func: () => R): R {
|
||||
return this.current().with(type, data).run(func);
|
||||
}
|
||||
|
||||
zoneData<T>(type: ZoneType): T | undefined {
|
||||
return this.current().data(type);
|
||||
}
|
||||
|
||||
current(): Zone {
|
||||
return this._asyncLocalStorage.getStore() ?? this._emptyZone;
|
||||
}
|
||||
|
||||
empty(): Zone {
|
||||
return this._emptyZone;
|
||||
}
|
||||
}
|
||||
const asyncLocalStorage = new AsyncLocalStorage<Zone | undefined>();
|
||||
|
||||
export class Zone {
|
||||
private readonly _asyncLocalStorage: AsyncLocalStorage<Zone | undefined>;
|
||||
private readonly _data: ReadonlyMap<ZoneType, unknown>;
|
||||
|
||||
static createEmpty(asyncLocalStorage: AsyncLocalStorage<Zone | undefined>) {
|
||||
return new Zone(asyncLocalStorage, new Map());
|
||||
}
|
||||
|
||||
private constructor(asyncLocalStorage: AsyncLocalStorage<Zone | undefined>, store: Map<ZoneType, unknown>) {
|
||||
constructor(asyncLocalStorage: AsyncLocalStorage<Zone | undefined>, store: Map<ZoneType, unknown>) {
|
||||
this._asyncLocalStorage = asyncLocalStorage;
|
||||
this._data = store;
|
||||
}
|
||||
|
@ -71,4 +48,8 @@ export class Zone {
|
|||
}
|
||||
}
|
||||
|
||||
export const zones = new ZoneManager();
|
||||
export const emptyZone = new Zone(asyncLocalStorage, new Map());
|
||||
|
||||
export function currentZone(): Zone {
|
||||
return asyncLocalStorage.getStore() ?? emptyZone;
|
||||
}
|
|
@ -30,7 +30,6 @@ export * from './utils/isomorphic/headers';
|
|||
export * from './utils/isomorphic/semaphore';
|
||||
export * from './utils/isomorphic/stackTrace';
|
||||
export * from './utils/zipFile';
|
||||
export * from './utils/zones';
|
||||
|
||||
export * from './server/utils/ascii';
|
||||
export * from './server/utils/comparators';
|
||||
|
@ -51,5 +50,6 @@ export * from './server/utils/spawnAsync';
|
|||
export * from './server/utils/task';
|
||||
export * from './server/utils/userAgent';
|
||||
export * from './server/utils/wsServer';
|
||||
export * from './server/utils/zones';
|
||||
|
||||
export { colors } from './utilsBundle';
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import { errors } from 'playwright-core';
|
||||
import { getPackageManagerExecCommand, monotonicTime, raceAgainstDeadline, zones } from 'playwright-core/lib/utils';
|
||||
import { getPackageManagerExecCommand, monotonicTime, raceAgainstDeadline, currentZone } from 'playwright-core/lib/utils';
|
||||
|
||||
import { currentTestInfo, currentlyLoadingFileSuite, setCurrentlyLoadingFileSuite } from './globals';
|
||||
import { Suite, TestCase } from './test';
|
||||
|
@ -266,7 +266,7 @@ export class TestTypeImpl {
|
|||
if (!testInfo)
|
||||
throw new Error(`test.step() can only be called from a test`);
|
||||
const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box });
|
||||
return await zones.run('stepZone', step, async () => {
|
||||
return await currentZone().with('stepZone', step).run(async () => {
|
||||
try {
|
||||
let result: Awaited<ReturnType<typeof raceAgainstDeadline<T>>> | undefined = undefined;
|
||||
result = await raceAgainstDeadline(async () => {
|
||||
|
|
|
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
|
||||
import * as playwrightLibrary from 'playwright-core';
|
||||
import { addInternalStackPrefix, asLocator, createGuid, debugMode, isString, jsonStringifyForceASCII, zones } from 'playwright-core/lib/utils';
|
||||
import { addInternalStackPrefix, asLocator, createGuid, currentZone, debugMode, isString, jsonStringifyForceASCII } from 'playwright-core/lib/utils';
|
||||
|
||||
import { currentTestInfo } from './common/globals';
|
||||
import { rootTestType } from './common/testType';
|
||||
|
@ -260,7 +260,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
// Some special calls do not get into steps.
|
||||
if (!testInfo || data.apiName.includes('setTestIdAttribute') || data.apiName === 'tracing.groupEnd')
|
||||
return;
|
||||
const zone = zones.zoneData<TestStepInternal>('stepZone');
|
||||
const zone = currentZone().data<TestStepInternal>('stepZone');
|
||||
if (zone && zone.category === 'expect') {
|
||||
// Display the internal locator._expect call under the name of the enclosing expect call,
|
||||
// and connect it to the existing expect step.
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
import {
|
||||
captureRawStack,
|
||||
createGuid,
|
||||
currentZone,
|
||||
isString,
|
||||
pollAgainstDeadline } from 'playwright-core/lib/utils';
|
||||
import { zones } from 'playwright-core/lib/utils';
|
||||
|
||||
import { ExpectError, isJestError } from './matcherHint';
|
||||
import {
|
||||
|
@ -380,7 +380,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
|||
try {
|
||||
setMatcherCallContext({ expectInfo: this._info, testInfo, step: step.info });
|
||||
const callback = () => matcher.call(target, ...args);
|
||||
const result = zones.run('stepZone', step, callback);
|
||||
const result = currentZone().with('stepZone', step).run(callback);
|
||||
if (result instanceof Promise)
|
||||
return result.then(finalizer).catch(reportStepError);
|
||||
finalizer();
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { captureRawStack, monotonicTime, sanitizeForFilePath, stringifyStackFrames, zones } from 'playwright-core/lib/utils';
|
||||
import { captureRawStack, monotonicTime, sanitizeForFilePath, stringifyStackFrames, currentZone } from 'playwright-core/lib/utils';
|
||||
|
||||
import { TimeoutManager, TimeoutManagerError, kMaxDeadline } from './timeoutManager';
|
||||
import { debugTest, filteredStackTrace, formatLocation, getContainedPath, normalizeAndSaveAttachment, trimLongString, windowsFilesystemFriendlyLength } from '../util';
|
||||
|
@ -245,7 +245,7 @@ export class TestInfoImpl implements TestInfo {
|
|||
}
|
||||
|
||||
private _parentStep() {
|
||||
return zones.zoneData<TestStepInternal>('stepZone')
|
||||
return currentZone().data<TestStepInternal>('stepZone')
|
||||
?? this._findLastStageStep(this._steps); // If no parent step on stack, assume the current stage as parent.
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue