Skip to content

Commit ee203b7

Browse files
authored
feat(evaluate): serialize map and set (#26730)
closes: #24040
1 parent 501ed32 commit ee203b7

File tree

6 files changed

+51
-3
lines changed

6 files changed

+51
-3
lines changed

packages/playwright-core/src/protocol/serializers.ts

+16
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ function innerParseSerializedValue(value: SerializedValue, handles: any[] | unde
7575
return BigInt(value.bi);
7676
if (value.r !== undefined)
7777
return new RegExp(value.r.p, value.r.f);
78+
if (value.m !== undefined)
79+
return new Map(innerParseSerializedValue(value.m, handles, refs));
80+
if (value.se !== undefined)
81+
return new Set(innerParseSerializedValue(value.se, handles, refs));
7882

7983
if (value.a !== undefined) {
8084
const result: any[] = [];
@@ -145,6 +149,10 @@ function innerSerializeValue(value: any, handleSerializer: (value: any) => Handl
145149
}
146150
return { s: `${error.name}: ${error.message}\n${error.stack}` };
147151
}
152+
if (isMap(value))
153+
return { m: innerSerializeValue(Array.from(value), handleSerializer, visitorInfo) };
154+
if (isSet(value))
155+
return { se: innerSerializeValue(Array.from(value), handleSerializer, visitorInfo) };
148156
if (isDate(value))
149157
return { d: value.toJSON() };
150158
if (isURL(value))
@@ -175,6 +183,14 @@ function innerSerializeValue(value: any, handleSerializer: (value: any) => Handl
175183
throw new Error('Unexpected value');
176184
}
177185

186+
function isMap(obj: any): obj is Map<any, any> {
187+
return obj instanceof Map || Object.prototype.toString.call(obj) === '[object Map]';
188+
}
189+
190+
function isSet(obj: any): obj is Set<any> {
191+
return obj instanceof Set || Object.prototype.toString.call(obj) === '[object Set]';
192+
}
193+
178194
function isRegExp(obj: any): obj is RegExp {
179195
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
180196
}

packages/playwright-core/src/protocol/validator.ts

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ scheme.SerializedValue = tObject({
5858
d: tOptional(tString),
5959
u: tOptional(tString),
6060
bi: tOptional(tString),
61+
m: tOptional(tType('SerializedValue')),
62+
se: tOptional(tType('SerializedValue')),
6163
r: tOptional(tObject({
6264
p: tString,
6365
f: tString,

packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export type SerializedValue =
2020
{ d: string } |
2121
{ u: string } |
2222
{ bi: string } |
23+
{ m: SerializedValue } |
24+
{ se: SerializedValue } |
2325
{ r: { p: string, f: string} } |
2426
{ a: SerializedValue[], id: number } |
2527
{ o: { k: string, v: SerializedValue }[], id: number } |
@@ -35,6 +37,14 @@ type VisitorInfo = {
3537

3638
export function source() {
3739

40+
function isMap(obj: any): obj is Map<any, any> {
41+
return obj instanceof Map || Object.prototype.toString.call(obj) === '[object Map]';
42+
}
43+
44+
function isSet(obj: any): obj is Set<any> {
45+
return obj instanceof Set || Object.prototype.toString.call(obj) === '[object Set]';
46+
}
47+
3848
function isRegExp(obj: any): obj is RegExp {
3949
try {
4050
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
@@ -94,6 +104,10 @@ export function source() {
94104
return new URL(value.u);
95105
if ('bi' in value)
96106
return BigInt(value.bi);
107+
if ('m' in value)
108+
return new Map(parseEvaluationResultValue(value.m));
109+
if ('se' in value)
110+
return new Set(parseEvaluationResultValue(value.se));
97111
if ('r' in value)
98112
return new RegExp(value.r.p, value.r.f);
99113
if ('a' in value) {
@@ -163,6 +177,11 @@ export function source() {
163177
if (typeof value === 'bigint')
164178
return { bi: value.toString() };
165179

180+
if (isMap(value))
181+
return { m: serialize(Array.from(value), handleSerializer, visitorInfo) };
182+
if (isSet(value))
183+
return { se: serialize(Array.from(value), handleSerializer, visitorInfo) };
184+
166185
if (isError(value)) {
167186
const error = value;
168187
if (error.stack?.startsWith(error.name + ': ' + error.message)) {

packages/protocol/src/channels.ts

+2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ export type SerializedValue = {
180180
d?: string,
181181
u?: string,
182182
bi?: string,
183+
m?: SerializedValue,
184+
se?: SerializedValue,
183185
r?: {
184186
p: string,
185187
f: string,

packages/protocol/src/protocol.yml

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ SerializedValue:
8282
u: string?
8383
# String representation of BigInt.
8484
bi: string?
85+
# JS representation of Map: [[key1, value1], [key2, value2], ...].
86+
m: SerializedValue?
87+
# JS representation of Set: [item1, item2, ...].
88+
se: SerializedValue?
8589
# Regular expression pattern and flags.
8690
r:
8791
type: object?

tests/page/page-evaluate.spec.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,14 @@ it('should transfer bigint', async ({ page }) => {
9999
expect(await page.evaluate(a => a, 17n)).toBe(17n);
100100
});
101101

102-
it('should transfer maps as empty objects', async ({ page }) => {
103-
const result = await page.evaluate(a => a.x.constructor.name + ' ' + JSON.stringify(a.x), { x: new Map([[1, 2]]) });
104-
expect(result).toBe('Object {}');
102+
it('should transfer maps', async ({ page }) => {
103+
expect(await page.evaluate(() => new Map([[1, { test: 42n }]]))).toEqual(new Map([[1, { test: 42n }]]));
104+
expect(await page.evaluate(a => a, new Map([[1, { test: 17n }]]))).toEqual(new Map([[1, { test: 17n }]]));
105+
});
106+
107+
it('should transfer sets', async ({ page }) => {
108+
expect(await page.evaluate(() => new Set([1, { test: 42n }]))).toEqual(new Set([1, { test: 42n }]));
109+
expect(await page.evaluate(a => a, new Set([1, { test: 17n }]))).toEqual(new Set([1, { test: 17n }]));
105110
});
106111

107112
it('should modify global environment', async ({ page }) => {

0 commit comments

Comments
 (0)