Skip to content

Commit f0a3a15

Browse files
authored
chore: explicitly reset apiZone instead of everything (#34265)
1 parent 1f2eb49 commit f0a3a15

File tree

5 files changed

+38
-33
lines changed

5 files changed

+38
-33
lines changed

packages/playwright-core/src/client/connection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class Connection extends EventEmitter {
138138
this._localUtils?._channel.addStackToTracingNoReply({ callData: { stack: frames, id } }).catch(() => {});
139139
// We need to exit zones before calling into the server, otherwise
140140
// when we receive events from the server, we would be in an API zone.
141-
zones.exitZones(() => this.onmessage({ ...message, metadata }));
141+
zones.empty().run(() => this.onmessage({ ...message, metadata }));
142142
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, apiName, type, method }));
143143
}
144144

packages/playwright-core/src/client/network.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ export class RouteHandler {
820820
this._times = times;
821821
this.url = url;
822822
this.handler = handler;
823-
this._svedZone = zones.currentZone();
823+
this._svedZone = zones.current().without('apiZone');
824824
}
825825

826826
static prepareInterceptionPatterns(handlers: RouteHandler[]) {

packages/playwright-core/src/client/waiter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class Waiter {
3535
constructor(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) {
3636
this._waitId = createGuid();
3737
this._channelOwner = channelOwner;
38-
this._savedZone = zones.currentZone();
38+
this._savedZone = zones.current().without('apiZone');
3939

4040
this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {});
4141
this._dispose = [

packages/playwright-core/src/utils/zones.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,54 +19,54 @@ import { AsyncLocalStorage } from 'async_hooks';
1919
export type ZoneType = 'apiZone' | 'expectZone' | 'stepZone';
2020

2121
class ZoneManager {
22-
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone|undefined>();
22+
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone | undefined>();
23+
private readonly _emptyZone = Zone.createEmpty(this._asyncLocalStorage);
2324

2425
run<T, R>(type: ZoneType, data: T, func: () => R): R {
25-
const zone = Zone._createWithData(this._asyncLocalStorage, type, data);
26-
return this._asyncLocalStorage.run(zone, func);
26+
return this.current().with(type, data).run(func);
2727
}
2828

2929
zoneData<T>(type: ZoneType): T | undefined {
30-
const zone = this._asyncLocalStorage.getStore();
31-
return zone?.get(type);
30+
return this.current().data(type);
3231
}
3332

34-
currentZone(): Zone {
35-
return this._asyncLocalStorage.getStore() ?? Zone._createEmpty(this._asyncLocalStorage);
33+
current(): Zone {
34+
return this._asyncLocalStorage.getStore() ?? this._emptyZone;
3635
}
3736

38-
exitZones<R>(func: () => R): R {
39-
return this._asyncLocalStorage.run(undefined, func);
37+
empty(): Zone {
38+
return this._emptyZone;
4039
}
4140
}
4241

4342
export class Zone {
4443
private readonly _asyncLocalStorage: AsyncLocalStorage<Zone | undefined>;
45-
private readonly _data: Map<ZoneType, unknown>;
44+
private readonly _data: ReadonlyMap<ZoneType, unknown>;
4645

47-
static _createWithData(asyncLocalStorage: AsyncLocalStorage<Zone|undefined>, type: ZoneType, data: unknown) {
48-
const store = new Map(asyncLocalStorage.getStore()?._data);
49-
store.set(type, data);
50-
return new Zone(asyncLocalStorage, store);
51-
}
52-
53-
static _createEmpty(asyncLocalStorage: AsyncLocalStorage<Zone|undefined>) {
46+
static createEmpty(asyncLocalStorage: AsyncLocalStorage<Zone | undefined>) {
5447
return new Zone(asyncLocalStorage, new Map());
5548
}
5649

57-
private constructor(asyncLocalStorage: AsyncLocalStorage<Zone|undefined>, store: Map<ZoneType, unknown>) {
50+
private constructor(asyncLocalStorage: AsyncLocalStorage<Zone | undefined>, store: Map<ZoneType, unknown>) {
5851
this._asyncLocalStorage = asyncLocalStorage;
5952
this._data = store;
6053
}
6154

55+
with(type: ZoneType, data: unknown): Zone {
56+
return new Zone(this._asyncLocalStorage, new Map(this._data).set(type, data));
57+
}
58+
59+
without(type?: ZoneType): Zone {
60+
const data = type ? new Map(this._data) : new Map();
61+
data.delete(type);
62+
return new Zone(this._asyncLocalStorage, data);
63+
}
64+
6265
run<R>(func: () => R): R {
63-
// Reset apiZone and expectZone, but restore stepZone.
64-
const entries = [...this._data.entries()].filter(([type]) => (type !== 'apiZone' && type !== 'expectZone'));
65-
const resetZone = new Zone(this._asyncLocalStorage, new Map(entries));
66-
return this._asyncLocalStorage.run(resetZone, func);
66+
return this._asyncLocalStorage.run(this, func);
6767
}
6868

69-
get<T>(type: ZoneType): T | undefined {
69+
data<T>(type: ZoneType): T | undefined {
7070
return this._data.get(type) as T | undefined;
7171
}
7272
}

tests/playwright-test/test-step.spec.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,8 +1376,9 @@ test('calls from waitForEvent callback should be under its parent step', {
13761376
await page.setContent('<div onclick="fetch(\\'/simple.json\\').then(r => r.text());">Go!</div>');
13771377
const responseJson = await test.step('custom step', async () => {
13781378
const responsePromise = page.waitForResponse(async response => {
1379-
const text = await response.text();
1380-
expect(text).toBeTruthy();
1379+
await page.content();
1380+
await page.content(); // second time a charm!
1381+
await expect(page.locator('div')).toContainText('Go');
13811382
return true;
13821383
});
13831384
@@ -1405,9 +1406,11 @@ pw:api |page.goto(${server.EMPTY_PAGE}) @ a.test.ts:4
14051406
pw:api |page.setContent @ a.test.ts:5
14061407
test.step |custom step @ a.test.ts:6
14071408
pw:api | page.waitForResponse @ a.test.ts:7
1408-
pw:api | page.click(div) @ a.test.ts:13
1409-
expect | expect.toBeTruthy @ a.test.ts:9
1410-
expect |expect.toBe @ a.test.ts:17
1409+
pw:api | page.click(div) @ a.test.ts:14
1410+
pw:api | page.content @ a.test.ts:8
1411+
pw:api | page.content @ a.test.ts:9
1412+
expect | expect.toContainText @ a.test.ts:10
1413+
expect |expect.toBe @ a.test.ts:18
14111414
hook |After Hooks
14121415
fixture | fixture: page
14131416
fixture | fixture: context
@@ -1464,7 +1467,8 @@ test('calls from page.route callback should be under its parent step', {
14641467
const response = await route.fetch();
14651468
const text = await response.text();
14661469
expect(text).toBe('');
1467-
await route.fulfill({ response })
1470+
await response.text(); // second time a charm!
1471+
await route.fulfill({ response });
14681472
});
14691473
await page.goto('${server.EMPTY_PAGE}');
14701474
});
@@ -1485,9 +1489,10 @@ fixture | fixture: page
14851489
pw:api | browserContext.newPage
14861490
test.step |custom step @ a.test.ts:4
14871491
pw:api | page.route @ a.test.ts:5
1488-
pw:api | page.goto(${server.EMPTY_PAGE}) @ a.test.ts:11
1492+
pw:api | page.goto(${server.EMPTY_PAGE}) @ a.test.ts:12
14891493
pw:api | apiResponse.text @ a.test.ts:7
14901494
expect | expect.toBe @ a.test.ts:8
1495+
pw:api | apiResponse.text @ a.test.ts:9
14911496
hook |After Hooks
14921497
fixture | fixture: page
14931498
fixture | fixture: context

0 commit comments

Comments
 (0)