k6 v0.48.0 is here 🎉! This release includes:

- Numerous long-awaited breaking changes.
- A new `k6 new` subcommand to generate a new test script.
- A new `k6/experimental/fs` module for file interactions.
- CPU and network throttling support for the k6 browser module.

## Breaking changes

This release includes several breaking changes, mainly cleaning up deprecations from previous versions. They should have a straightforward migration process, and not heavily impact existing users.

- [#3448](https://github.com/grafana/k6/pull/3448) limits metric names, aligning to both OpenTelemetry (OTEL) and Prometheus name requirements, while still being limited to 128 ASCII characters. Warnings about the limit started in [v0.45](https://github.com/grafana/k6/releases/tag/v0.45.0).
- [#3439](https://github.com/grafana/k6/pull/3439) changes the `Client` signature in `k6/experimental/redis` module. Refer to the module-related section below.
- [#3350](https://github.com/grafana/k6/pull/3350) removes the `grpc.invoke()`'s parameter `headers`, deprecated in k6 [v0.37](https://github.com/grafana/k6/releases/tag/v0.37.0). Use the `metadata` parameter instead.
- [#3389](https://github.com/grafana/k6/pull/3389) removes the `--logformat` flag, deprecated in [v0.38](https://github.com/grafana/k6/releases/tag/v0.38.0). Use the `--log-format` flag instead.
- [#3390](https://github.com/grafana/k6/pull/3390) removes all CSV output's CLI arguments, deprecated in [v0.35](https://github.com/grafana/k6/releases/tag/v0.35.0). This change makes the CSV output consistent with other output formats.
- [#3365](https://github.com/grafana/k6/pull/3365) removes the `k6 convert` CLI command, deprecated in [v0.41](https://github.com/grafana/k6/releases/tag/v0.41.0). Use the [har-to-k6](https://github.com/grafana/har-to-k6) package instead.
- [#3451](https://github.com/grafana/k6/pull/3451) removes logic that would attempt to prepend a `https://` scheme to module specifiers that were not recognized. Deprecated in k6 [v0.25](https://github.com/grafana/k6/releases/tag/v0.25.0). Use full URLs if you want to load remote modules instead.

## New features

### Add `k6 new` subcommand [#3394](https://github.com/grafana/k6/pull/3394)

`k6` now has a `new` subcommand that generates a new test script. This is useful for new users who want to get started quickly, or for experienced users who want to save time when creating new test scripts. To use the subcommand, open your terminal and type:

```bash
k6 new [filename]
```

If no filename is provided, k6 uses `script.js` as the default filename. The subcommand will create a new file with the provided name in the current directory, and populate it with a basic test script that can be run with `k6 run`.

### Add a `k6/experimental/fs` module [#3165](https://github.com/grafana/k6/pull/3165)

`k6` now has a new `k6/experimenal/fs` module providing a memory-efficient way to handle file interactions within your test scripts. It currently offers support for opening files, reading their content, seeking through it, and retrieving metadata about them.

Unlike the traditional [open](https://grafana.com/docs/k6/latest/javascript-api/init-context/open/) function, which loads a file multiple times into memory, the filesystem module reduces memory usage by loading the file as little as possible, and sharing the same memory space between all VUs. This approach significantly reduces the memory footprint of your test script and lets you load and process large files without running out of memory.

For more information, refer to the [module documentation](https://grafana.com/docs/k6/latest/javascript-api/k6-experimental/fs/).

<details>
<summary> Expand to see an example of the new functionality.</summary>

This example shows the new module usage:

```javascript
import fs from 'k6/experimental/fs';

// k6 doesn't support async in the init context. We use a top-level async function for `await`.
//
// Each Virtual User gets its own `file` copy.
// So, operations like `seek` or `read` won't impact other VUs.
let file;
(async function () {
  file = await open('bonjour.txt');
})();

export default async function () {
  // About information about the file
  const fileinfo = await file.stat();
  if (fileinfo.name != 'bonjour.txt') {
    throw new Error('Unexpected file name');
  }

  const buffer = new Uint8Array(128);

  let totalBytesRead = 0;
  while (true) {
    // Read into the buffer
    const bytesRead = await file.read(buffer);
    if (bytesRead == null) {
      // EOF
      break;
    }

    // Do something useful with the content of the buffer
    totalBytesRead += bytesRead;

    // If bytesRead is less than the buffer size, we've read the whole file
    if (bytesRead < buffer.byteLength) {
      break;
    }
  }

  // Check that we read the expected number of bytes
  if (totalBytesRead != fileinfo.size) {
    throw new Error('Unexpected number of bytes read');
  }

  // Seek back to the beginning of the file
  await file.seek(0, SeekMode.Start);
}
```

</details>

### Redis (m)TLS support and new Client constructor options [#3439](https://github.com/grafana/k6/pull/3439), [xk6-redis/#17](https://github.com/grafana/xk6-redis/pull/17)

In this release, the `k6/experimental/redis` module receives several important updates, including breaking changes.

#### Connection URLs

The `Client` constructor now supports connection URLs to configure connections to Redis servers or clusters. These URLs can be in the format `redis://[[username][:password]@][host][:port][/db-number]` for standard connections, or `rediss://[[username][]:password@]][host][:port][/db-number]` for TLS-secured connections. For more details, refer to the [documentation](https://grafana.com/docs/k6/latest/javascript-api/k6-experimental/redis/).

##### Example usage

```javascript
import redis from 'k6/experimental/redis';

const redisClient = new redis.Client('redis://someusername:somepassword@localhost:6379/0');
```

#### Revamped Options object

The `Client` constructor has been updated with a new [Options](https://grafana.com/docs/k6/latest/javascript-api/k6-experimental/redis/redis-options/) object format. This change aligns the module with familiar patterns from Node.js and Deno libraries, offering enhanced flexibility and control over Redis connections. For more details, refer to the [documentation](https://grafana.com/docs/k6/latest/javascript-api/k6-experimental/redis/redis-options/).

<details>
<summary> Expand to see an example of the new functionality.</summary>

This example shows the usage of the new `Options` object:

```javascript
import redis from 'k6/experimental/redis';

const redisClient = new redis.Client({
  socket: {
    host: 'localhost',
    port: 6379,
  },
  username: 'someusername',
  password: 'somepassword',
});
```
</details>

#### (m)TLS support

The Redis module now includes (m)TLS support, enhancing security for connections. This update also improves support for Redis clusters and sentinel modes (failover). For connections using self-signed certificates, enable k6's [insecureSkipTLSVerify](https://grafana.com/docs/k6/latest/using-k6/k6-options/reference/#insecure-skip-tls-verify) option (set to `true`).

<details>
<summary> Expand to see an example of the new functionality.</summary>

This example shows the configuration of a TLS connection:

```javascript
import redis from 'k6/experimental/redis';

const redisClient = new redis.Client({
  socket: {
    host: 'localhost',
    port: 6379,
    tls: {
      ca: [open('ca.crt')],
      cert: open('client.crt'), // client certificate
      key: open('client.key'), // client private key
    },
  },
});
```
</details>

### Add tracing instrumentation [#3445](https://github.com/grafana/k6/pull/3445)

`k6` now supports a new *traces output* option that allows you to configure the output for traces generated during its execution. This option can be set through the `--traces-output` argument in the `k6 run` command or by setting the `K6_TRACES_OUTPUT` environment variable.

Currently, no traces are generated by `k6` itself, but this feature represents the first step towards richer tracing functionalities in `k6` and its extensions.

By default traces output is set to `none`, and currently the only supported output is `otel` which uses the [opentelemetry-go](https://github.com/open-telemetry/opentelemetry-go)'s [Open Telemetry](https://opentelemetry.io/) API and SDK implementations. The format for the `otel` *traces output* configuration is the following:

```
--traces-output=<endpoint>[,opt1=val1,opt2=val2]
```

Where `opt`s can be one of the following options:
* `proto`: Specifies the protocol to use in the connection to the traces backend. Supports `grpc` *(default)* and `http`.
* `header.<header_name>`: Specifies an additional header to include in the connection to the traces backend.

Example:

```
K6_TRACES_OUTPUT=https://traces.k6.io/v1/traces,proto=http,header.Authorization=Bearer token
```

### Add support for browser module's `page.throttleCPU` [browser#1095](https://github.com/grafana/xk6-browser/pull/1095)

The browser module now supports throttling the CPU from chrome/chromium's perspective by using the `throttleCPU` API, which helps emulate slower devices when testing the website's frontend. It requires an argument of type `CPUProfile`, which includes a `rate` field that is a slow-down factor, where `1` means no throttling, `2` means 2x slowdown, and so on. For more details, refer to the [documentation](https://grafana.com/docs/k6/v0.48.x/javascript-api/k6-experimental/browser/page/throttlecpu).

```js
...
  const context = browser.newContext();
  const page = context.newPage();

  try {
    page.throttleCPU({ rate: 4 });
...
```

### Add support for browser module's `page.throttleNetwork` [browser#1094](https://github.com/grafana/xk6-browser/pull/1094)

The browser module now supports throttling the characteristics of the network from chrome/chromium's perspective by using the `throttleNetwork` API, which helps emulate slow network connections when testing the website's frontend. It requires an argument of type `NetworkProfile`, with a definition of:

```ts
export interface NetworkProfile {
    /*
     * Minimum latency from request sent to response headers received (ms).
     */
    latency: number;
    
    /*
     * Maximal aggregated download throughput (bytes/sec). -1 disables download
     * throttling.
     */
    download: number;
    
    /*
     * Maximal aggregated upload throughput (bytes/sec). -1 disables upload
     * throttling.
     */
    upload: number;
}
```

You can either define your own network profiles or use the ones we have defined by importing `networkProfiles` from the `browser` module. For more details, refer to the [documentation](https://grafana.com/docs/k6/v0.48.x/javascript-api/k6-experimental/browser/page/throttlenetwork).

```js
import { browser, networkProfiles } from 'k6/experimental/browser';
...
  const context = browser.newContext();
  const page = context.newPage();

  try {
    page.throttleNetwork(networkProfiles['Slow 3G']);
...
```

## k6's documentation is moving under [grafana.com/docs/k6](https://grafana.com/docs/k6)

It's not directly part of the k6 v0.48 release, but we believe it is worth mentioning that we're moving the documentation from [k6.io/docs](https://k6.io/docs/) to [grafana.com/docs/k6](https://grafana.com/docs/k6).

The legacy documentation space `k6.io/docs` will be available for a while, but we encourage you to update your bookmarks and links to the new domain.

## UX improvements and enhancements

- [browser#1074](https://github.com/grafana/xk6-browser/pull/1074) adds a new `browser.closeContext()` [method](https://grafana.com/docs/k6/v0.48.x/javascript-api/k6-experimental/browser/closecontext) to facilitate closing the current active browser context.
- [#3370](https://github.com/grafana/k6/pull/3370) adds a new flag `--profiling-enabled` which enables exposing [pprof](https://go.dev/blog/pprof) profiling endpoints. The profiling endpoints are exposed on the same port as the HTTP REST API under the `/debug/pprof/` path. This can be useful for extension developers.
- [#3442](https://github.com/grafana/k6/pull/3442) adds a new `--version` flag, which has the same output as `k6 version` command. Thanks, @ffapitalle!
- [#3423](https://github.com/grafana/k6/pull/3423) adds an environment variable `K6_INFLUXDB_PROXY` to the InfluxDB output which allows specifying proxy. Thanks, @IvanovOleg!
- [#3398](https://github.com/grafana/k6/pull/3398) enables k6 cloud traces by default.
- [#3400](https://github.com/grafana/k6/pull/3400) sets a binary-based cloud output (a.k.a. cloud output v2) as the default version for streaming metrics from a local test run via `-o cloud`.
- [#3452](https://github.com/grafana/k6/pull/3452) adds `fsext.Abs` helper function.

## Bug fixes

- [#3380](https://github.com/grafana/k6/pull/3380) corrects `console.debug()`, aligning `-v` output to `--console-output` and `stdout`.
- [#3416](https://github.com/grafana/k6/pull/3416) prints the stack trace when there's an exception in `handleSummary()`.
- [#3438](https://github.com/grafana/k6/pull/3438) prevents an error on HTTP requests with `content-encoding` header and HTTP statuses known for having no body.
- [browser#1077](https://github.com/grafana/xk6-browser/pull/1077) fixes `browserContext.clearPermissions` to clear permissions without panic.
- [browser#1042](https://github.com/grafana/xk6-browser/pull/1042) fixes `browserContext.waitForEvent` which involved promisifying the `waitForEvent` API.
- [browser#1078](https://github.com/grafana/xk6-browser/pull/1078) fixes request interception deadlock to improve stability.
- [browser#1101](https://github.com/grafana/xk6-browser/pull/1101) fixes `page.$` so that it returns `null` when no matches with given selector are found.
- [#3397](https://github.com/grafana/k6/pull/3397), [#3427](https://github.com/grafana/k6/pull/3427), [#3417](https://github.com/grafana/k6/pull/3417) update `goja` dependency. Fixes a possible panic and proper handling circular types at `JSON.stringify`. Fixes an issue about dumping the correct stack trace when an error is re-thrown.
- [browser#1106](https://github.com/grafana/xk6-browser/pull/1106) fixes an NPE on NavigateFrame when navigate occurs in the same document.
- [browser#1096](https://github.com/grafana/xk6-browser/pull/1096) fixes a panic when trying to interact within nested `iframe`s. Thanks, @bandorko!

## Maintenance and internal improvements

- [#3378](https://github.com/grafana/k6/pull/3378) fixes usage of `gh` in GitHub actions creating the OSS release.
- [#3386](https://github.com/grafana/k6/pull/3386), [#3387](https://github.com/grafana/k6/pull/3387), [#3388](https://github.com/grafana/k6/pull/3388), [browser#1047](https://github.com/grafana/xk6-browser/pull/1047) update dependencies.
- [#3393](https://github.com/grafana/k6/pull/3393), [#3399](https://github.com/grafana/k6/pull/3399) fix lint issues in the `js` package.
- [#3381](https://github.com/grafana/k6/pull/3381) disables temporarily ARM tests on GitHub Actions.
- [#3401](https://github.com/grafana/k6/pull/3401), [#3469](https://github.com/grafana/k6/pull/3469) refactors a Makefile, removes `make ci-like-lint` in favor of `make lint`. Updates a golangci-lint version to v1.55.2.
- [#3410](https://github.com/grafana/k6/pull/3410) fixes the `tests` reference in the all rule of the Makefile. Thanks, @flyck!
- [#3402](https://github.com/grafana/k6/pull/3402) adds a test-case for the `k6 cloud`.
- [#3421](https://github.com/grafana/k6/pull/3421) updates dependencies for xk6 integration tests.
- [browser#1075](https://github.com/grafana/xk6-browser/pull/1075), [browser#1076](https://github.com/grafana/xk6-browser/pull/1076) refactors `clearPermissions` and `grantPermissions`.
- [browser#1043](https://github.com/grafana/xk6-browser/pull/1043) refines tests.
- [browser#1069](https://github.com/grafana/xk6-browser/pull/1069), [browser#1090](https://github.com/grafana/xk6-browser/pull/1090) refactor internal.
- [browser#1102](https://github.com/grafana/xk6-browser/pull/1102) uses `force` and `noWaitAfter` in `frame.newAction`.
- [#3443](https://github.com/grafana/k6/pull/3443) mentions that k6-core team aims to support the last two major golang versions for building a k6 binary.
- [#3437](https://github.com/grafana/k6/pull/3437) switches k6 cloud traces to a new hostname.
- [#3429](https://github.com/grafana/k6/pull/3429) increases timeout expectations for the `TestSetupTimeout` test.
- [#3446](https://github.com/grafana/k6/pull/3446) moves log tokenizer to `lib/strvals` package.

## Roadmap

### Graduating from experimental

It has been a while since we've introduced the `k6/experimental` namespace. This namespace was specifically created to test new features before we fully committed to them. Thanks to it, we have been able to iterate on features and receive valuable feedback from the community before adding them to the core of k6.

In the following releases, we're going to graduate `k6/experimental/grpc` and `k6/experimental/timers`.

These modules' "experimental" versions will remain available for a couple of releases, but the goal is to remove the "experimental" imports for them in favor of the core-only imports.

### New dashboard features

We're happy to announce our work on a new, upcoming dashboard feature. Based on the [xk6-dashboard](https://github.com/grafana/xk6-dashboard) extension, this upcoming feature will enable you to visualize your test runs and their results in your web browser, in real time. The k6 maintainers team is starting to work towards its integration into the core of k6, and we're aiming to release it in the next couple of releases.

While the final user-experience might differ, you can already try it out by following the instructions in the [xk6-dashboard](https://github.com/grafana/xk6-dashboard) repository. We update the extension on a regular basis as we're converging towards the first release of the feature in k6. Go ahead and give it a try! Let us know what you think about it!