mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user