DevExpress / testcafe

A Node.js tool to automate end-to-end web testing.
https://testcafe.io
MIT License
9.82k stars 670 forks source link

Minified Bundle (terser) Produces Error: 500 Internal Server Error #7356

Closed htho closed 1 year ago

htho commented 1 year ago

What is your Scenario?

I have some code that fails when minified with terser, but works when not. I binary-searched the compress flags to find that the combination of the compress options reduce_vars and unused lead to code that causes the hammerhead web server to produce an error 500: GET http://XXX.XXX.XXX.XXX:50331/XXX!s!utf-8/http://localhost:3000/script.js net::ERR_ABORTED 500 (Internal Server Error)

Then I binary-searched the bundle to create a snippet that causes the error:

const singleton = new class {
    someMap=new Map;
    dosomething() {
        foo.map(({x, y}) => "doesnt matter")
    }
};

Unfortunately I was not able to reduce it any further. Although I would not write that by hand, its valid JavaScript.

What is the Current behavior?

hammerhead sends Error: 500 Internal Server Error

What is the Expected behavior?

No Error

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

I created a repository: https://github.com/htho/testcafe-repro-error-500-terser

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
</body>
<script src="script.js"></script>
</html>
// script.js

// this is the part that makes hammerhead produce error 500.
// I was not able to reduce the example any further.
const singleton = new class {
    someMap=new Map;
    dosomething() {
        foo.map(({x, y}) => "doesnt matter")
    }
};

// this should make the test pass
document.body.insertAdjacentHTML("afterbegin", `<h1>It works!</h1>`)

What is your TestCafe test code?

import { fixture, Selector } from "testcafe";

fixture("internalServerError500")
    .page("http://localhost:3000/")

test("script was executed", async (t) => {
    await t.debug(); // take a look at the browser console
    await t.expect(Selector("h1").exists).ok();
});

Your complete configuration file

none

Your complete test report

PS C:\XXX\github.com\htho\testcafe-repro-error-500-terser> npm run test

> repro-testcafe-error-500-terser@1.0.0 test
> testcafe chrome test.tc.ts

 Running tests in:
 - Chrome 106.0.0.0 / Windows 10

 internalServerError500
 × script was executed

   1) AssertionError: expected false to be truthy

      Browser: Chrome 106.0.0.0 / Windows 10

         3 |fixture("internalServerError500")
         4 |    .page("http://localhost:3000/")
         5 |
         6 |test("script was executed", async (t) => {
         7 |    // await t.debug(); // take a look at the browser console
       > 8 |    await t.expect(Selector("h1").exists).ok();
         9 |});

         at <anonymous> (C:\XXX\github.com\htho\testcafe-repro-error-500-terser\test.tc.ts:8:43)
         at <anonymous> (C:\XXX\github.com\htho\testcafe-repro-error-500-terser\test.tc.ts:8:71)
         at __awaiter (C:\XXX\github.com\htho\testcafe-repro-error-500-terser\test.tc.ts:4:12)
         at <anonymous> (C:\XXX\github.com\htho\testcafe-repro-error-500-terser\test.tc.ts:6:41)

 1/1 failed (5s)

Screenshots

No response

Steps to Reproduce

  1. git clone https://github.com/htho/testcafe-repro-error-500-terser.git
  2. cd testcafe-repro-error-500-terser
  3. npm i
  4. npm run serve
  5. npm run test (in another terminal)

TestCafe version

2.0.1

Node.js version

v16.17.1

Command-line arguments

testcafe chrome test.tc.ts

Browser name(s) and version(s)

chrome 106

Platform(s) and version(s)

Windows 10

Other

No response

AlexKamaev commented 1 year ago

Thank you for sharing the example. I managed to reproduce the issue and admit that everything works as expected in the Chrome browser. However, this kind of code is very uncommon, so it will take time to research the cause of the issue inside testcafe-hammerhead. Until we fix the issue, please use some other terser settings for testing purposes if it's possible.

htho commented 1 year ago

this kind of code is very uncommon

I have to admit that singletons are a bad practice, but they are not uncommon.

// original code
class SomeSingleton {
    someMap = new Map();
    /** @param {{x: number, y: number}[]} coords */
    swap(coords) {
        return coords.map(({x, y}) => ({y: x, x: y}))
    }
}
export const singleton = new SomeSingleton();
// minified code
export const singleton = new class {
    someMap=new Map;
    swap(e) {
        return e.map((({x: e, y: n}) => ({
            y: e,
            x: n
        })));
    }
};
// terser options
{
  module: true,
  compress: {},
  mangle: {},
  output: {beautify: true},
  parse: {},
  rename: {},
}

You can reproduce it on https://try.terser.org/

I am looking forward to see the fix, because this is very interesting.

AlexKamaev commented 1 year ago

I have to admit that singletons are a bad practice, but they are not uncommon.

I meant that the resulting code after minification looks very uncommon, so we cannot correctly process it in testcafe-hammerhead.

I am looking forward to see the fix, because this is very interesting.

I'm afraid we cannot give you precise estimates as to when the issue will be fixed. Due to its complexity, it may take a while.

htho commented 1 year ago

I only saw testcafe-hammerhead add some headers/footers to the bundle, so I was surprised to find out that the actual text-content matters on the server side. Thats what I meant by "interesting".

AlexKamaev commented 1 year ago

testcafe-hammerhead not only injects code to the target page but modifies javascript code as well. That's why terser throws an error. The testcafe-hammerhead module just cannot process the uncommon code as you mentioned.

AlexKamaev commented 1 year ago

duplicate of https://github.com/DevExpress/testcafe/issues/6988

htho commented 1 year ago

Hi,

I am not sure if they are the same, or just similar.

In #6988 the file is delivered, but not processed properly. In this Bug, the server fails with Error 500.

I need to know which syntax exactly leads to these problems. In the worst case scenario I need to enable some babel plugins to transpile class Syntax to something TestCafe understands.

AlexKamaev commented 1 year ago

The cause of both issues is similar. They occur due to the incorrect processing of class construction. So, we will take these cases into account when we start fixing it.

benlind commented 1 year ago

I am also running into this. PayPal seems to have updated their SDK JS to include class syntax, and since our page loads the SDK TestCafe is returning a 500 for the script.

This is on TestCafe 2.3.0.

AlexKamaev commented 1 year ago

@benlind Thank you for sharing this information. I believe the issue can be fixed with the --experimental-proxyless option if you run your test suite in Chrome. Could you please check if there is any error in your tests if you run TestCafe as follows:

npx testcafe chrome test.js --experimental-proxyless

Please refer to the following article for details: https://testcafe.io/documentation/404237/guides/experimental-capabilities/proxyless-mode

htho commented 1 year ago

I created a branch of my original repo, with --experimental-proxyless: https://github.com/htho/testcafe-repro-error-500-terser/tree/proxyless

The tests dont fail anymore.

Still: What are the long term goals with proxyless? The main reason I use testcafe, is that it can be used to tests mobile devices. (testcafe-browser-provider-android and testcafe-browser-provider-idevices). Will (something like) this be possible in the future?

AlexKamaev commented 1 year ago

@htho Thank you for testing your usage scenario in --experimental-proxyless mode.

Still: What are the long term goals with proxyless?

Using the proxyless mode will significantly increase the speed of test execution. So, the proxyless mode is one of our prior goals.

The main reason I use testcafe, is that it can be used to tests mobile devices.

Currently, we are developing the proxyless mode for Chrome and Chrome-based browsers on Desktop devices.

htho commented 1 year ago

As of TestCafe 2.6.2 this is fixed.