microsoft / microsoft/playwright

[Bug]: Recorder/codegen and page bindings break when the page defines Array.prototype.toJSON

Open
#41,359 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

P3-collecting-feedback
Dominant language
TypeScript
Stars
96.3k
Forks
6.5k
Avg merge
1d 6h
Merged PRs (30d)
180

Description

### Version

1.61.0

### Steps to reproduce

**Recorder:**
1. Run `npx playwright codegen` and navigate to any page (even `about:blank`).
2. In the browser's DevTools Console, paste the kind of serializer Prototype.js installs:
```js
Array.prototype.toJSON = function () {
var results = [];
this.forEach(function (value) {
results.push(typeof value === 'object' ? JSON.stringify(value) : String(value));
});
return '[' + results.join(', ') + ']';
};
```
3. Click any element. Recording stops with the error below.

**Deterministic (no UI):** minimal repo https://github.com/whgksqls9999/pw-tojson-recorder-repro
```bash
git clone https://github.com/whgksqls9999/pw-tojson-recorder-repro
cd pw-tojson-recorder-repro
npm install
npx playwright install chromium
npx playwright test repro.spec.ts
```

### Expected behavior

When an element is clicked, codegen tracks it and records the corresponding action.

### Actual behavior

```
Error: serializedArgs is not an array. This can happen when Array.prototype.toJSON is defined incorrectly
at _PageBinding.dispatch
at _Page.onBindingCalled
at _FrameSession._onBindingCalled
```

The recorder is unusable on such a page.

### Additional context

Root cause: the injected binding controller serializes its payload with the page's `JSON.stringify`, so a page-defined `Array.prototype.toJSON` turns the internal `serializedArgs` array into a string and `PageBinding.dispatch` rejects it.

I completely understand the decision in #32508 to surface a clear log rather than handle the error. The one thing I'd gently raise is the user experience: a developer whose app has such an incorrect `Array.prototype.toJSON` override might try `codegen`, see that clicks do nothing, and — without opening DevTools — assume *"maybe this just doesn't fit our app"* and move on. That's partly on the developer for not checking, of course, but it can be an easy silent dead end. Would you be open to revisiting this case, or do you feel the current approach is the one to keep?

As a heavily simplified illustration, something like the patch below worked in my testing. If you feel this direction is worth pursuing, I'd be glad to refine it as needed and take the work forward.

[bindingsController.ts]
```ts
private _stringifyPayload(payload: BindingPayload): string {
const arrayToJSON = Array.prototype.toJSON;
const objectToJSON = Object.prototype.toJSON;
try {
delete Array.prototype.toJSON;
delete Object.prototype.toJSON;
return JSON.stringify(payload);
} finally {
if (arrayToJSON !== undefined) Array.prototype.toJSON = arrayToJSON;
if (objectToJSON !== undefined) Object.prototype.toJSON = objectToJSON;
}
}

(this._global as any)[this._globalBindingName](
this._stringifyPayload(payload),
);
```

### Environment

```shell
System:
OS: Windows 10 Home 10.0.19045
CPU: (16) x64 12th Gen Intel(R) Core(TM) i5-12500H
Memory: 1.54 GB / 15.71 GB
Binaries:
Node: 22.20.0
npm: 10.9.3
npmPackages:
@playwright/test: 1.61.0 => 1.61.0
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with bindingsController.ts and the PageBinding.dispatch path described in the report, then run the linked repro.spec.ts reproduction after installing Chromium. Trace how the page's Array.prototype.toJSON changes the binding payload and verify that recorder clicks are recorded without the serializedArgs error.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
testing, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.