Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] codegen serializedArgs can be of type string (should be object) perhaps caused by backward compat bug #21786

Closed
chrisjsimpson opened this issue Mar 19, 2023 · 2 comments

Comments

@chrisjsimpson
Copy link

chrisjsimpson commented Mar 19, 2023

Background: Working with an ancient hardware embedded web ui (circa 2012) which uses the prototypejs framework.

Playwright version

npx playwright --version
Version 1.31.2

Test case (happy path)

npx playwright codegen http://127.0.0.1:

<!DOCTYPE html>
<html>
	<head></head>
	<body>
		<form>
			Name <input type="text" name="name" />
			<input type="submit" />
		</form>
	</body>
</html>

Produces the following events:

What I'm doing is clicking the <input type="text" name="name" /> and typing the word 'test' into that form control.
This is working as expected:

serializedArgs is type object
[{"o":[{"k":"name","v":"click"},{"k":"selector","v":"internal:role=textbox"},{"k":"position","v":{"v":"undefined"}},{"k":"signals","v":{"a":[],"id":2}},{"k":"button","v":"left"},{"k":"modifiers","v":0},{"k":"clickCount","v":1}],"id":1}]
args
args is type object
[
  {
    name: 'click',
    selector: 'internal:role=textbox',
    position: undefined,
    signals: [],
    button: 'left',
    modifiers: 0,
    clickCount: 1
  }
]
ok
serializedArgs
serializedArgs is type object
[]
args
args is type object
[]
ok
serializedArgs
serializedArgs is type object
[]
args
args is type object
[]
ok
serializedArgs
serializedArgs is type object
[{"o":[{"k":"name","v":"fill"},{"k":"selector","v":"internal:role=textbox"},{"k":"signals","v":{"a":[],"id":2}},{"k":"text","v":"t"}],"id":1}]
args
args is type object
[
  {
    name: 'fill',
    selector: 'internal:role=textbox',
    signals: [],
    text: 't'
  }
]
ok
serializedArgs
serializedArgs is type object
[{"o":[{"k":"name","v":"fill"},{"k":"selector","v":"internal:role=textbox"},{"k":"signals","v":{"a":[],"id":2}},{"k":"text","v":"te"}],"id":1}]
args
args is type object
[
  {
    name: 'fill',
    selector: 'internal:role=textbox',
    signals: [],
    text: 'te'
  }
]
ok
serializedArgs
serializedArgs is type object
[{"o":[{"k":"name","v":"fill"},{"k":"selector","v":"internal:role=textbox"},{"k":"signals","v":{"a":[],"id":2}},{"k":"text","v":"tes"}],"id":1}]
args
args is type object
[
  {
    name: 'fill',
    selector: 'internal:role=textbox',
    signals: [],
    text: 'tes'
  }
]
ok
serializedArgs
serializedArgs is type object
[{"o":[{"k":"name","v":"fill"},{"k":"selector","v":"internal:role=textbox"},{"k":"signals","v":{"a":[],"id":2}},{"k":"text","v":"test"}],"id":1}]
args
args is type object
[
  {
    name: 'fill',
    selector: 'internal:role=textbox',
    signals: [],
    text: 'test'
  }
]
ok
serializedArgs
serializedArgs is type object
[]
args
args is type object
[]
ok

Sad path with prototypejs: serializedArgs can be of type string (should be object)

Please note I have not pasted in the prototypejs application, apologies.

When clicking or entering (any event) playwright page.js throws the following, because for some reason (quirks mode type thing? I don't know) causes serializedArgs to be of string type:

Uncaught (in promise) TypeError: serializedArgs.map is not a function
    at Function.dispatch node_modules/playwright-core/lib/server/page.js:609:37)
    at Page._onBindingCalled (node_modules/playwright-core/lib/server/page.js:218:23)
    at FrameSession._onBindingCalled (node_modules/playwright-core/lib/server/chromium/crPage.js:717:37)

Event log: Here I also type the word test but into a different (much older) prototype application embeded in hardware:

Notice that serializedArgs is a string but should be an object.

npx playwright codegen http://192.168.1.1:

serializedArgs
serializedArgs is type string
"[]"
[]
ok
serializedArgs
serializedArgs is type string
"[{\"o\": [{\"k\": \"name\", \"v\": \"click\"}, {\"k\": \"selector\", \"v\": \"#user\"}, {\"k\": \"position\", \"v\": {\"v\": \"undefined\"}}, {\"k\": \"signals\", \"v\": {\"a\": [], \"id\": 2}}, {\"k\": \"button\", \"v\": \"left\"}, {\"k\": \"modifiers\", \"v\": 0}, {\"k\": \"clickCount\", \"v\": 1}], \"id\": 1}]"
[{"o": [{"k": "name", "v": "click"}, {"k": "selector", "v": "#user"}, {"k": "position", "v": {"v": "undefined"}}, {"k": "signals", "v": {"a": [], "id": 2}}, {"k": "button", "v": "left"}, {"k": "modifiers", "v": 0}, {"k": "clickCount", "v": 1}], "id": 1}]
ok
serializedArgs
serializedArgs is type string
"[]"
[]
ok
serializedArgs
serializedArgs is type string
"[{\"o\": [{\"k\": \"name\", \"v\": \"fill\"}, {\"k\": \"selector\", \"v\": \"#user\"}, {\"k\": \"signals\", \"v\": {\"a\": [], \"id\": 2}}, {\"k\": \"text\", \"v\": \"t\"}], \"id\": 1}]"
[{"o": [{"k": "name", "v": "fill"}, {"k": "selector", "v": "#user"}, {"k": "signals", "v": {"a": [], "id": 2}}, {"k": "text", "v": "t"}], "id": 1}]
ok
serializedArgs
serializedArgs is type string
"[{\"o\": [{\"k\": \"name\", \"v\": \"fill\"}, {\"k\": \"selector\", \"v\": \"#user\"}, {\"k\": \"signals\", \"v\": {\"a\": [], \"id\": 2}}, {\"k\": \"text\", \"v\": \"te\"}], \"id\": 1}]"
[{"o": [{"k": "name", "v": "fill"}, {"k": "selector", "v": "#user"}, {"k": "signals", "v": {"a": [], "id": 2}}, {"k": "text", "v": "te"}], "id": 1}]
ok
serializedArgs
serializedArgs is type string
"[]"
[]
ok
serializedArgs
serializedArgs is type string
"[{\"o\": [{\"k\": \"name\", \"v\": \"fill\"}, {\"k\": \"selector\", \"v\": \"#user\"}, {\"k\": \"signals\", \"v\": {\"a\": [], \"id\": 2}}, {\"k\": \"text\", \"v\": \"tes\"}], \"id\": 1}]"
[{"o": [{"k": "name", "v": "fill"}, {"k": "selector", "v": "#user"}, {"k": "signals", "v": {"a": [], "id": 2}}, {"k": "text", "v": "tes"}], "id": 1}]
ok
serializedArgs
serializedArgs is type string
"[{\"o\": [{\"k\": \"name\", \"v\": \"fill\"}, {\"k\": \"selector\", \"v\": \"#user\"}, {\"k\": \"signals\", \"v\": {\"a\": [], \"id\": 2}}, {\"k\": \"text\", \"v\": \"test\"}], \"id\": 1}]"
[{"o": [{"k": "name", "v": "fill"}, {"k": "selector", "v": "#user"}, {"k": "signals", "v": {"a": [], "id": 2}}, {"k": "text", "v": "test"}], "id": 1}]
ok
serializedArgs
serializedArgs is type string

The crude 'fix' in playwright-core/lib/server/page.js

tldr JSON.parse serializedArgs to an object. (but why has serializedArgs become a string?).

  • change serializedArgs from const to mutable
  • serializedArgs = JSON.parse(serializedArgs); to an object
  static async dispatch(page, payload, context) {
    let {
      name,
      seq,
      serializedArgs
    } = JSON.parse(payload);
    try {
      (0, _utils.assert)(context.world);
      const binding = page.getBinding(name);
      let result;
      if (binding.needsHandle) {
        const handle = await context.evaluateHandle(takeHandle, {
          name,
          seq
        }).catch(e => null);
        result = await binding.playwrightFunction({
          frame: context.frame,
          page,
          context: page._browserContext
        }, handle);
      } else {
        console.log("ok");
        console.log("serializedArgs");
        console.log("serializedArgs is type", typeof(serializedArgs));
        console.log(JSON.stringify(serializedArgs));
        if ( typeof(serializedArgs) === 'string' ) {
          console.log("Converting serializedArgs into an object")

          try {
            serializedArgs = JSON.parse(serializedArgs);
            console.log(serializedArgs);
          } catch (e) {
            console.error("Unable to parse serializedArgs:", e);
          }
          console.log(serializedArgs);
        }
        debugger;

I hope that helps, appreciate this is a very niche usecase however took me a long time to root cause so someone else may benefit if they're hitting this

  • are we hitting some background compatibility /pollyfills perhaps?
  • I've not been able to identify why / when serializedArgs gets stringified

p.s. I would appreciate if someone would direct me how to use debugger; & --inspect --debug-brk within the context of playwright codegen it would save a lot of time.

@mxschmitt
Copy link
Member

Great investigation! Unfortunately we cannot act on it without seeing a website in front of us. Here are some hints how you can debug it:

  • often its caused that a website is e.g. overriding String/JSON/Error/Object prototypes, then the serialisation logic behaves wrongly, because their polyfills are not spec compliant
  • The bug can be either on the browser side, where it gets serialised - or on the Node.js side where it gets deserialised, I think you already know that its on the browser side.
  • What I would recommend next, is checkout our repo, build it, run watch mode, add some debugger; statements.
  • Then when you run code, open Devtools in the browser and your debugger; or console.log statements should trigger!

We are more than happy to accept a patch with a bugfix for your site to make codegen more reliable for you and other users!

@aslushnikov
Copy link
Collaborator

Let me close this for now so that we don't accumulate stale issues.

If you find a repro that we can run locally & debug, please do not hesitate to file a new issue to us!

@chrisjsimpson chrisjsimpson changed the title [Bug] serializedArgs can be of type string (should be object) perhaps caused by backward compat bug [Bug] codegen serializedArgs can be of type string (should be object) perhaps caused by backward compat bug May 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants