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

No support for optional chaining syntax #2714

Closed
niba opened this issue Nov 23, 2021 · 29 comments · Fixed by #2755
Closed

No support for optional chaining syntax #2714

niba opened this issue Nov 23, 2021 · 29 comments · Fixed by #2755

Comments

@niba
Copy link

niba commented Nov 23, 2021

What is your Scenario?

I have a site which loads a code with the following line

this._pageLabels?.[this._currentPageNumber - 1] ?? null;

HH transforms it to the

__get$(this._pageLabels,this._currentPageNumber-1) ?? null;

and this code throws an error during runtime

Uncaught (in promise) Error: Cannot read property '0' of null at Function.t._error (hammerhead.js:12) at value (hammerhead.js:12)

The logic in transformed code is different.

I managed to get it working by extending createPropertyGetWrapper / createComputedPropertyGetWrapper with optional flag and changing the logic of method but I don't know if this is a correct solution.

The site is an internal one so I cannot share the url

What is the Current behavior?

HH transformed code with optional chaining syntax have the different logic than the original one

What is the Expected behavior?

HH should detect optional chaining and perform a proper transformation

What is your public website URL? (or attach your complete example)

Private

What is your TestCafe test code?

I've been using proxy

Your complete configuration file

No response

Your complete test report

No response

Screenshots

No response

Steps to Reproduce

  1. Find a site with optional chaining code
  2. Try to use proxy on the site
  3. Site code breaks

TestCafe version

testcafe-hammerhead 24.2.1

Node.js version

No response

Command-line arguments

Browser name(s) and version(s)

No response

Platform(s) and version(s)

No response

Other

No response

@felis2803
Copy link
Contributor

Hello @niba,

Thank you for your report. I have reproduced this bug. Stay tuned for updates in this issue

@felis2803
Copy link
Contributor

For the team.

Example to reproduce:

<button>Click me!</button>
<script>
    function getLastBoxText () {
        const lastBox = this.boxes?.[this.length -1] ?? { text: 'Click me, please!' };

        return lastBox.text;
    }

    const container = {
        boxes:  null,
        length: 0,

        getLastBoxText
    }

    document.querySelector('button').innerText = container.getLastBoxText();
</script>

@raphaelniederer
Copy link

We have the same problem.

Error message in the console:

Error: Cannot read property 'href' of undefined
    at Function.t._error (hammerhead.js:12:31820)
    at t._propertyGetter (hammerhead.js:13:539)

Code:

export const isLinkEvent = (event?: AnyEventObject): event is LinkEvent => !!event?.data?.href;

event is undefined in this case.

We have this issue since Testcafe 10.0.2 - so we are still on 10.0.1

Is there a workaround for this problem?

@miherlosev
Copy link
Contributor

Hi @raphaelniederer

At present, there is no workaround. We will update this thread as soon we have any news.

@danieltroger
Copy link
Contributor

Also suffering from this issue.

const $42170c0be024d7ea$export$a4f97b27a0592e6c = (url)=>{debugger;return (url ? new URL(url, window.location.origin) : window.location).pathname.split("/").filter((a)=>a).pop()?.replace(/\\.html?$/i, "") || ""}

is being transformed to

const $42170c0be024d7ea$export$a4f97b27a0592e6c = (url)=>{debugger;return  __call$((url?new URL(url,__get$(window,"location").origin):__get$(window,"location")).pathname.split("/").filter(a=>a).pop(),"replace",[/\.html?$/i,""])  || ""}

Which fails with:

Error: Cannot call method 'replace' of undefined
    at Function.t._error (hammerhead.js:formatted:15811:23)
    at value (hammerhead.js:formatted:15829:43)
    at $7727f36b03cf11cc$export$8d42ce4827f43582.$42170c0be024d7ea$export$a4f97b27a0592e6c (eval at <anonymous> (hammerhead.js:formatted:15175:41), <anonymous>:1713:76)
    at $7727f36b03cf11cc$export$8d42ce4827f43582.eval (eval at <anonymous> (hammerhead.js:formatted:15175:41), <anonymous>:734:38)
    at $7727f36b03cf11cc$export$eee4c4d924348b05 (eval at <anonymous> (hammerhead.js:formatted:15175:41), <anonymous>:1942:33)
    at new $7727f36b03cf11cc$export$8d42ce4827f43582 (eval at <anonymous> (hammerhead.js:formatted:15175:41), <anonymous>:1932:48)
    at eval (eval at <anonymous> (hammerhead.js:formatted:15175:41), <anonymous>:1966:51)
    at eval (eval at <anonymous> (hammerhead.js:formatted:15175:41), <anonymous>:5018:3)

@danieltroger
Copy link
Contributor

Is there any documentation on this transformation, why is it necessary? Can I turn it off somehow?

@felis2803
Copy link
Contributor

Hello @danieltroger,

This transformation is necessary for hiding some DOM nodes from client scripts of the tested website. TestCafe uses this feature to render its own UI on the tested web page (for example, this). It cannot be disabled.

@danieltroger
Copy link
Contributor

I don't think the sites I'm testing on would care about extra DOM nodes but thanks for explaining. Opted for switching testcafe to a transpiled version of my script as a workaround.

@miherlosev
Copy link
Contributor

miherlosev commented Feb 7, 2022

Opted for switching testcafe to a transpiled version of my script as a workaround.

We intend to fix the bug as soon as possible, so I would recommend that you wait for the fix.

@LironHazan
Copy link

@miherlosev Hey, can you pls tell if this is the root cause of the issue I recently opened? the transformation I was having was a bit different - without any guard.
Appreciate the great work you're doing!

@miherlosev
Copy link
Contributor

Hi @LironHazan

The cause of the DevExpress/testcafe#6863 is a problem with processing optional chaining syntax.

@github-actions
Copy link

No updates yet. Once we get any results, we will post them in this thread.

@f5io
Copy link

f5io commented Mar 30, 2022

hi everyone, this became an issue for us after updating sveltekit, none of our tests would run as sveltekit couldn't initialise.

we've found an interim solution until this is resolved upstream....

by applying the following patch, with patch-package, we were able to get our full test suite running 🎉

diff --git a/node_modules/testcafe-hammerhead/lib/processing/script/transformers/computed-property-get.js b/node_modules/testcafe-hammerhead/lib/processing/script/transformers/computed-property-get.js
index cde9fe1..b461f37 100644
--- a/node_modules/testcafe-hammerhead/lib/processing/script/transformers/computed-property-get.js
+++ b/node_modules/testcafe-hammerhead/lib/processing/script/transformers/computed-property-get.js
@@ -39,6 +39,15 @@ const transformer = {
         // for(object[prop] in source)
         if (parent.type === esotope_hammerhead_1.Syntax.ForInStatement && parent.left === node)
             return false;
+        
+        if (node.property.type === 'PrivateIdentifier')
+            return false
+
+        if (parent.type === 'LogicalExpression' && parent.operator === '??')
+            return false;
+
+        if (parent.type === 'ChainExpression')
+            return false;
         return true;
     },
     run: node => node_builder_1.createComputedPropertyGetWrapper(node.property, node.object)

to achieve this for yourself, follow these steps:

  1. install patch-package as a dev dependency
  2. go into node_modules/testcafe-hammerhead/lib/processing/script/transformers/computed-property-get.js, and add the following lines to the condition function...
if (node.property.type === 'PrivateIdentifier')
   return false

if (parent.type === 'LogicalExpression' && parent.operator === '??')
  return false;

if (parent.type === 'ChainExpression')
  return false;
  1. run patch-package testcafe-hammerhead, this will produce a file patches/testcafe-hammerhead+24.5.14.patch which you should commit
  2. add a postinstall script to your package.json that calls patch-package

i would happily open a PR to resolve this issue, but it seems there are types spread out across multiple packages that dont correlate, we were unable to get this to pass typescript type-checking without enforcing ts-ignore on the lines added.

@Aleksey28
Copy link
Collaborator

Hi @f5io,

Thank you for your cooperation. We will research this issue and update this thread once we have any news.

@LironHazan
Copy link

@f5io Nice!!
we've workaround the issue by a temporary eslint rule which yields errors for node.optional && node.computed and transforms it to use lodash instead
fixer.replaceText(node, _.get(${object}, ${property}))
But instrumenting a patch is a good idea..

@f5io
Copy link

f5io commented Apr 6, 2022

I believe there is still an outstanding issue with nullish coalescing that has not been fixed by #2755.

the following js should not blow up during a testcafe run...

const obj = {};
const key = 'foo';
const b = obj[key] ?? 'bar';

you could consider member access to 'foo' here optional however the AST does not reflect that in the MemberExpression node, as can be seen in this AST Explorer Link

if you would prefer I can open a new issue...

@Aleksey28
Copy link
Collaborator

Hi @f5io

You are right. This fix doesn't address the ?? operator. Please create a separate issue for the ?? operator. The current issue is related to the optional chaining operator.

@danieltroger
Copy link
Contributor

I just glanced at the tests of your MR and didn't see a test for ({ fn: null })?.fn?.();

Just wanted to ask if you thought about that case?

@f5io
Copy link

f5io commented Apr 7, 2022

Hi @Aleksey28,

Apologies, on closer inspection it looks like the changes to _propertyGetter should have resolved the issues around nullish coalescing also. I could not create a failing test-case against the package. Hopefully this should unblock testcafe against sveltekit applications. Good work all 👍

@Aleksey28
Copy link
Collaborator

@danieltroger, Thank you for the example. I tried to implement it, and it didn't throw an error. It should work. Please use this PR to check it.

@f5io, I checked it, and it works without any errors. Thank you for looking into this.

@AlexanderZhig
Copy link

AlexanderZhig commented Apr 20, 2022

Hi guys!
I take TC version 1.18.6, that uses testcafe-hammerhead": "24.5.18" and the problem is still present:
I get the following code
d=(t&&t["function.from"])??null
converted to
d=t&&t["function.from"]??null
that leads to
Uncaught SyntaxError: Unexpected token '??'

@AlexanderZhig
Copy link

Hi guys! I take TC version 1.18.6, that uses testcafe-hammerhead": "24.5.18" and the problem is still present: I get the following code d=(t&&t["function.from"])??null converted to d=t&&t["function.from"]??null that leads to Uncaught SyntaxError: Unexpected token '??'

Any comments on this?

@miherlosev
Copy link
Contributor

Hi @AlexanderZhig,

See the future updates related to this issue at DevExpress/testcafe#6995.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.