grafana / k6

A modern load testing tool, using Go and JavaScript - https://k6.io
GNU Affero General Public License v3.0
25.69k stars 1.26k forks source link

Can't run certain test file - Unexpected token, expected ( (36978:8) (TypeScript/Webpack/Babel) #3264

Open GeorgeXCV opened 1 year ago

GeorgeXCV commented 1 year ago

Brief summary

I am using this template for my project https://github.com/grafana/k6-template-typescript

One test runs fine, but in another, I got like 50 module errors, so had to add them to my webpack config and install them manually. Now the other test file runs but I get an error related to an unexpected token but I don't have any for loops in my project. Can you help me fix this error please?

Here is my test file that runs fine without any problem:

import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
  http.get('https://test.dev/');

  http.get('https://test.dev/abc');

  sleep(3);

  http.get('https://test.dev/1234');

  sleep(3);
}

k6 version

k6 javascript module

OS

13.5

Docker version and image (if applicable)

No response

Steps to reproduce the problem

Here is the test file I get an unexpected token error:

import http from 'k6/http';
import { sleep, check } from 'k6';
import { getCloudflareToken } from '../GoogleCloud';

export default async function () {
  const email = `test+${Date.now()}@test.com`;
  const cf_token = await getCloudflareToken('development');

  const payload = {
    email: email,
    "cf_token": cf_token,
  };

  const url = 'https://test.com/sign-up';
  const headers = { 'Content-Type': 'application/json' };
  const response = http.post(url, JSON.stringify(payload), { headers: headers });

  check(response, {
    'Sign-up successful': (res) => res.status === 200 && res.json('$.success') === true,
  });

  // Wait for a short time between sign-up requests
  sleep(1);
}

Here is the GoogleCloud file:

// Import the Secret Manager client and instantiate it:
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
const client = new SecretManagerServiceClient();

export async function getCloudflareToken(env: string) { 
  const projectID = env === "development" ? "123" : "456";
  const [version] = await client.accessSecretVersion({ name: `projects/${projectID}/secrets/CLOUDFLARE_TOKEN/versions/1` });
  if (version.payload && version.payload.data) {
    return version.payload.data.toString();
  } else {
    throw new Error("Payload data is missing or undefined.");
  }
}

Here is the package.json:

{
  "name": "load-testing",
  "version": "1.0.0",
  "description": "",
  "main": "unauthenticated-users.ts",
  "scripts": {
    "build": "webpack",
    "sign-up-freemium": "k6 run dist/sign-up.js",
    "unauthenticated-users": "k6 run --vus 100 --duration 5m dist/unauthenticated-users.js"
  },
  "author": "George",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "7.22.10",
    "@babel/plugin-proposal-class-properties": "7.18.6",
    "@babel/plugin-proposal-object-rest-spread": "7.13.8",
    "@babel/preset-env": "7.13.15",
    "@babel/preset-typescript": "7.13.0",
    "@google-cloud/secret-manager": "^5.0.0",
    "@types/k6": "~0.45.0",
    "@types/webpack": "5.28.0",
    "assert": "^2.0.0",
    "babel-loader": "8.2.2",
    "browserify-tls": "^1.0.0",
    "browserify-zlib": "^0.2.0",
    "buffer": "^6.0.3",
    "clean-webpack-plugin": "4.0.0-alpha.0",
    "copy-webpack-plugin": "^9.0.1",
    "crypto-browserify": "^3.12.0",
    "https-browserify": "^1.0.0",
    "net-browserify": "^0.2.4",
    "node-forge": "^1.3.1",
    "os-browserify": "^0.3.0",
    "path-browserify": "^1.0.1",
    "querystring-es3": "^0.2.1",
    "stream-browserify": "^3.0.0",
    "stream-http": "^3.2.0",
    "typescript": "4.2.4",
    "url": "^0.11.1",
    "util": "^0.12.5",
    "webpack": "^5.88.2",
    "webpack-cli": "4.6.0",
    "webpack-glob-entries": "^1.0.1"
  }
}

Expected behaviour

Sign up test file runs successfully, just like my other test file.

Actual behaviour

I get the following error when I run sign up test:

WARN[0000] The source for `file:///Users/georgeashton/Documents/nansen-load-testing/dist/sign-up.js` needs to go through babel but is over 256000 bytes. For performance reasons source map support will be disabled for this particular file. 
ERRO[0128] SyntaxError: file:///Users/george/Documents/load-testing/dist/sign-up.js: Unexpected token, expected ( (36978:8)
  36976 |     let length = 0;
  36977 |     const chunks = [];
> 36978 |     for await (const chunk of stream) {
        |         ^
  36979 |         length += chunk.length;
  36980 |         chunks.push(chunk);
  36981 |     }
        at <internal/k6/compiler/lib/babel.min.js>:2:28536(99)
        at <internal/k6/compiler/lib/babel.min.js>:14:24413(11)
        at bound  (native)
        at s (<internal/k6/compiler/lib/babel.min.js>:1:1327(8))  hint="script exception"

 *  The terminal process "/bin/zsh '-l', '-c', 'npm run sign-up-freemium'" terminated with exit code: 107. 
oleiade commented 1 year ago

Hey @GeorgeXCV 👋🏻

Sorry for the delay in addressing this. The notification slipped under my radar.

I believe you're experiencing a lack of support for the for-await syntax in k6's JS runtime: goja. I'm reading that the for-await syntax is part of the ECMAScript 2018-2019 specification, but our JS runtime is only compatible with ES5.1, and mostly compatible with ES6.

@mstoykov might have more insights into this as he is the maintainer who works the most actively on the runtime, and might be able to offer some alternatives 🙇🏻

mstoykov commented 1 year ago

for-await of is in practice the way to iterate async iterable objects.

But those are basically not supported in goja. The commit adding generators specifically only added synchrnous ones and I have no idea why the async iterables weren't added.

Currently I will recommedn using babel to transpile the code, but the exact configuration will be more involved.

On the other hand I will expect that even if you manage to get this working @google-cloud/secret-manager will want to make some kind of http request at some point and it will likely try an fetch API which k6 does not support.

So unfortunately my proposal will be to write the http requests needed to access secret manager by hand using the API k6 provides. We have done something similar for aws in https://github.com/grafana/k6-jslib-aws, but I don't think anyone has shared something similar for google