Releases: grafana/k6
v0.49.0
k6 v0.49.0
is here 🎉! This release:
- Adds a built-in web dashboard that displays test results in real time.
- Introduces
clear
functionality to the browser module'slocator
classes. - Merges the gRPC experimental module back into the gRPC core module.
- Enables the ability to get the selection from an element in
k6/html
. - Collects internal modules and outputs used by a script.
- Prepares
k6/experimental/timers
for stabilization.
Breaking changes
- #3494 stops updating
loadimpact/k6
docker image. If you still use it, please migrate to the grafana/k6 image. - browser#1111 removes
timeout
option forisVisible
andisHidden
since the API no longer waits for the element to appear on the page.
New features
Web Dashboard
The new web dashboard brings real-time visualization to load testing. This feature allows users to monitor test progress and analyze
results dynamically, enhancing the overall testing experience.
Real-time test results
Activate this feature using the environment variable K6_WEB_DASHBOARD=true
. For this initial release, the dashboard is not enabled by default to allow users to opt into this new experience as it evolves.
K6_WEB_DASHBOARD=true k6 run script.js
Once enabled and the test script is running, navigate to http://localhost:5665 in your web browser to access the dashboard.
Test report
The web dashboard also offers an HTML test report (see an example) for detailed analysis, enabling easy sharing and downloading capabilities for
collaboration.
To access and download the report, click on the Report button in the dashboard's top right corner or use the K6_WEB_DASHBOARD_EXPORT
environment variable.
K6_WEB_DASHBOARD=true K6_WEB_DASHBOARD_EXPORT=test-report.html k6 run script.js
Add clear
to the locator
class browser#1149
The new clear
method on the locator
class clears the text boxes and input fields. This is useful when navigating to a website where the text boxes and input fields already contain a value that needs to be cleared before filling it with a specific value.
Expand to see an example of the new functionality.
import { check } from 'k6';
import { browser } from 'k6/experimental/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
}
export default async function() {
const context = browser.newContext();
const page = context.newPage();
await page.goto('https://test.k6.io/my_messages.php', { waitUntil: 'networkidle' });
// To mimic an input field with existing text.
page.locator('input[name="login"]').type('admin');
check(page, {
'not_empty': p => p.locator('input[name="login"]').inputValue() != '',
});
// Clear the text.
page.locator('input[name="login"]').clear();
check(page, {
'empty': p => p.locator('input[name="login"]').inputValue() == '',
});
page.close();
}
Add tracing to the browser module browser#1100
The browser module now generates traces that provide a representation of its inner workings, such as API methods executed (for example browser.newPage
and page.goto
), page navigations, and Web Vitals measurements.
Currently, the instrumented methods are a subset of all the methods exposed by the browser module API, but this will be extended in the future.
The traces generation for the browser module depends on the overall k6
traces option introduced in v0.48.0. Check out the documentation to learn more about it.
gRPC streaming API becomes part of the k6 core #3490
With this release, gRPC's streaming API becomes part of the core's k6/net/grpc
module. The experimental k6/experimental/grpc
has been back-merged into the core.
You can still use import k6/experimental/grpc
for a couple of releases, but it's deprecated and will be removed in the future (planned in k6 version v0.51.0
).
To migrate your scripts, replace k6/experimental/grpc
with k6/net/grpc
in your script imports, and the code should work as before.
k6/html: Extract selection from element #3519
k6/html
has been around for a while and allows you to search within an HTML document with a jQuery-like API called Selection, and also has support for the more standard Element that represents DOM element.
For a long time, you could get an Element from a Selection using the .get(index)
, but you couldn't get back to a Selection from an Element.
This is not a common case, but one that requires quite a bit of code. For example, see the following jQuery snippet:
let li = http.get("https://test.k6.io").html().find("li");
li.each(function(_, element) {
// here element is an element not a selection
// but what if for each li we want to select something more?
// in jquery that will be:
let container = $(element).closest('ul.header-icons');
// but what should `$` do?
// in a browser there is only 1 html document that you have access to
// in k6 though you can be working with multiple ones, so `$` can't know which one it should
// work against
});
In order to support the above example, you can use selection
, without going to the element:
let li = http.get("https://test.k6.io").html().find("li");
for (; li.size() > 0; li = li.next()) {
let ul = li.closest('ul.header-icons'); // li here is still a selection and we iterate over it.
}
This is not always possible though, and arguably isn't what most users will naturally do.
Because of this, we have now added a new .selection()
which returns a selection for its element.
let li = http.get("https://test.k6.io").html().find("li");
li.each(function(_, element) {
let container = element.selection().closest('ul.header-icons');
// .. more code
});
Thanks to @Azhovan! 🙇 🎉
Collect usage data on imported internal modules and outputs #3525
k6 now collects usage data of the modules and outputs that are being used when the usage report is enabled. The data collection is only related to the built-in k6 modules and outputs. Private, custom modules and extensions are never collected. The usage report is enabled by default in k6, but it is possible to opt-out using the no-usage-report option.
We always want to improve the product, but at the same time, we need to pay attention to where we allocate our resources. Having data of what are the most used modules and outputs gives us better confidence to make decisions because we are supported by data.
The data can let us know what percentage of our users will benefit from the introduction of a new feature and also, how many of them would be impacted in case of a breaking change.
UX improvements and enhancements
- #3529 enables the k6 cloud traces output by default.
- #3440 adds a fallback for using built-in certificates if the OS provides none. Thanks to
@mem
for working on it! - browser#1104 adds support for browser module traces metadata. Users can define key-value metadata that will be included as attributes in every generated span.
- browser#1135 improves the array output from
console
in the k6 logs. - browser#1137, browser#1145 improves the error messages displayed when Chrome or Chromium isn't found.
- #3543 replaces documentation URLs to
grafana.com/docs/k6/latest/
.
Bug fixes
- #3485 fixes the REST API always logging a 200 status code response, which was found as part of fixing lint issues in the code.
- browser#1129 mitigates the risk of panics when the website under test uses the
console
. - [browser#1133](https://github.com/graf...
v0.48.0
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 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.
- #3439 changes the
Client
signature ink6/experimental/redis
module. Refer to the module-related section below. - #3350 removes the
grpc.invoke()
's parameterheaders
, deprecated in k6 v0.37. Use themetadata
parameter instead. - #3389 removes the
--logformat
flag, deprecated in v0.38. Use the--log-format
flag instead. - #3390 removes all CSV output's CLI arguments, deprecated in v0.35. This change makes the CSV output consistent with other output formats.
- #3365 removes the
k6 convert
CLI command, deprecated in v0.41. Use the har-to-k6 package instead. - #3451 removes logic that would attempt to prepend a
https://
scheme to module specifiers that were not recognized. Deprecated in k6 v0.25. Use full URLs if you want to load remote modules instead.
New features
Add k6 new
subcommand #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:
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
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 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.
Expand to see an example of the new functionality.
This example shows the new module usage:
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);
}
Redis (m)TLS support and new Client constructor options #3439, xk6-redis/#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.
Example usage
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 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.
Expand to see an example of the new functionality.
This example shows the usage of the new Options
object:
import redis from 'k6/experimental/redis';
const redisClient = new redis.Client({
socket: {
host: 'localhost',
port: 6379,
},
username: 'someusername',
password: 'somepassword',
});
(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 option (set to true
).
Expand to see an example of the new functionality.
This example shows the configuration of a TLS connection:
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
},
},
});
Add tracing instrumentation #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's Open Telemetry 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. Supportsgrpc
(default) andhttp
.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
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.
...
const context = browser.newContext();
const page = context.newPage();
try {
page.throttleCPU({ rate: 4 });
...
Add support for browser module's page.throttleNetwork
browser#1094
The browser module now supports throttling the characteristics of the network from chrome/chromium's perspective by using the ...
v0.47.0
k6 v0.47.0
is here 🎉! This release includes:
Deprecations
- #3347 The built-in
statsd
output option has been deprecated, and users should use the xk6-output-statsd extension instead. See #2982 for future plans. - #3288 Loading remote modules now requires users to prepend them with
https://
. Before, k6 would try to resolve importing remote modules by prependinghttps://
if it was missing. This behavior has been deprecated and will be fully removed in the next release (v0.48.0).
New features
Add gRPC's binary metadata support #3234, xk6-grpc#46
The k6 gRPC modules (k6/net/grpc
and k6/experimental/grpc
) now support handling binary metadata that uses the -bin
postfix, according to the gRPC specification.
let resp = client.invoke("grpc.testing.TestService/EmptyCall", {}, { metadata: { "X-Load-Tester-bin": new Uint8Array([2, 200]) } })
Thanks to @sapphire-janrain for the contribution!
Add gRPC's reflection metadata support #3343, xk6-grpc#46
The k6 gRPC modules (k6/net/grpc
and k6/experimental/grpc
) now support adding metadata to reflection requests by using a new connection parameter reflectMetadata
.
Higher precision for Trend metrics in Grafana Cloud k6 #3302
Grafana Cloud k6 is now able to store and visualize Trend metrics up to 3 digits of precision for decimal numbers.
Docker support for browser-based tests #3199
k6 is now publishig Docker images that include Chromium web browser. This allows k6 users to run tests that use Browser API without having to install Chrome first. Check the "A note on running browser tests" section of the Overview page on DockerHub for details.
Docker images for ARM64 architecture #3320
The k6's release process now builds and pushes dedicated Docker images for ARM64. Check k6's tags page on DockerHub for details.
New authentication methods and HTTP headers API for Prometheus remote write output xk6-output-prometheus-remote#143, xk6-output-prometheus-remote#145, xk6-output-prometheus-remote#147
The experimental Prometheus remote write output now supports two new authentication methods: Bearer token and TLS certificates. Check out the documentation to learn more about how to define them using the new environment variables.
We've also added the K6_PROMETHEUS_RW_HTTP_HEADERS
that defines a new and more convenient way to set custom HTTP headers to pass through each flush metrics' request.
Improved the browser module's cookie API
The browser module now provides a more complete and robust API for handling cookies. The cookie API was stabilized by defining a new Cookie
class (browser#1008, browser#1030) that can be used while creating and retrieving cookies. This enabled us to add a new browserContext.cookies([urls])
method (browser#1005) that returns all cookies from the current browser context. The new API also supports filtering cookies by URL (browser#1016).
That led to fixing a bug where the expires
field was not being set correctly while adding cookies using the context.addCookie()
method (browser#1031). Lastly, the existing context.clearCookies()
method was fixed to clear all cookies from the current browser context (browser#1040).
const context = browser.newContext();
context.addCookies([
{name: 'foo', value: 'bar', url: 'https://test.k6.io'},
{name: 'baz', value: 'qux', url: 'https://grafana.com'},
]);
const cookies = context.cookies('https://test.k6.io');
console.log(cookies.length); // 1
console.log(cookies[0].name); // foo
console.log(cookies[0].value); // bar
context.clearCookies();
console.log(context.cookies.length); // 0
Add support for browser module's page.on('console')
browser#1006
Allows users to register a handler to be executed every time the console
API methods are called from within the page's JavaScript context. The arguments passed into the handler are defined by the ConsoleMessage class.
page.on('console', msg => {
check(msg, {
'assertConsoleMessageType': msg => msg.type() == 'log',
'assertConsoleMessageText': msg => msg.text() == 'this is a console.log message 42',
'assertConsoleMessageArgs0': msg => msg.args()[0].jsonValue() == 'this is a console.log message',
'assertConsoleMessageArgs1': msg => msg.args()[1].jsonValue() == 42,
});
});
page.evaluate(() => console.log('this is a console.log message', 42));
UX improvements and enhancements
- #3338, xk6-grpc#48 Adds support for the gRPC reflection protocol v1.
- #3290 Adds error logging when executing
setup
andteardown
via REST API. Thanks to @kmtym1998 for the contribution! - #3327 Adds commit identifier for the k6 build when running
k6 version
. - #3340 Updates k6
*-with-browser
Docker images to automatically set theno-sandbox
environment variable. - #3335 The character limit for metric names increased from 63 to 128 after the OpenTelemetry update. k6 will return an error starting on the next release (v0.48.0) if users hit the limit.
- browser#1007 Adds a
k6
object (window.k6 = {};
) to help identify k6 browser module tests. - browser#1022 Refactors the
check
inexamples/fillform.js
so that it matches the type definitions and documentation forcheck
.
Bug fixes
- xk6-grpc#47 Fixes the premature closing of a gRPC stream when a stream's client has finished sending. Thanks to @thiagodpf for reporting!
- #3344, xk6-grpc#49 Adds support for Google's protobuf wrappers. Thanks to @zibul444 for reporting!
- #3308 Updates
goja
version, and fixes a compiler bug when a class is declared in a function with an argument. - browser#1039 Fixes
goja
conversions while adding and retrieving cookies. - browser#1038 Fixes read/write data race for edge case with remote browsers.
- browser#1034 Fixes
page.reload
&page.setContent
to use the default navigation timeout over the default timeout. - browser#1033 Fixes the
page
timeouts so it is actually used after being set.
Maintenance and internal improvements
- #3342 Updates xk6-grpc to the latest version. This change brings all the latest fixes and improvements to the experimental gRPC module.
- #3271,#3272 Updates the golangci version and adds the
interfacebloat
linter. - #3279 Fixes the CI not publishing the SBOM file on a new release.
- #3283 Updates the Go version in k6's CI used to build the binaries.
- #3341, #3339 Updates
goja
, includes runtime initialization speed-up and a fix for source indexes. - #3311 Updates the
alpine
image version that is used as the base of the k6 Docker image. - browser#1043, browser#1021, browser#1019, browser#1014 Fixes xk6-browser tests.
- browser#1000, [browser#10...
v0.46.0
k6 v0.46
is here 🎉! This release includes:
- xk6-browser extension version bump to
v1.0.2
, which includes breaking changes. - Send custom headers to Loki log output
- Cloud Traces, the new integration between k6 and Tempo
- Ability to configure TLS per gRPC connection
- Cloud output v2
- And many UX improvements, bug fixes, and internal improvements
Breaking Changes
Browser
In this release, the xk6-browser extension version is bumped up to v1.0.2
, as it includes multiple breaking changes concerning options, browser lifecycle, and metrics. See the migration guide for making your test scripts compatible with the new version.
Options devtools
, env
and proxy
are deprecated (browser#868, browser#870, browser#872). Additionally, browser launch
/connect
options are no longer defined in the corresponding JS API methods, instead the test execution related options are now defined inside the browser scenario options (see #3036), and the other more "environmental options", such as headless
, debug
, executablePath
, are set as ENV vars. Also, the slowMo
option is no longer supported, although it might be supported again in the future through a different API (browser#876).
Metrics also went through a few changes. The Web Vitals metrics are renamed to use the browser_
prefix and short namings (e.g.: webvital_first_input_delay
-> browser_web_vital_fid
) (browser#885, browser#903), and the rating metric is removed, as it is now set as a label in the corresponding Web Vitals metrics (browser#915).
The browser HTTP metrics have also been modified, as these are now split from the HTTP module ones, so there are new browser_
prefixed HTTP metrics, specifically for request duration and failed requests (browser#916).
Another big change introduced in this version affects the way the browser lifecycle is handled. Users no longer have to explicitly initialize/close the browser instance through the JS API. Instead, a browser instance will be automatically initialized/closed at the beginning/end of each iteration if the browser type is set in scenario options (see #3036). This also means that the chromium
entity from k6/experimental/browser
import path is no longer valid. Instead, the browser
entity provides access to the browser methods such as browser.newPage()
(browser#910, browser#944). This change means that the browser.on()
method is no longer applicable, and therefore it has been removed (browser#919).
Additionally, related to import changes, the browser module version is no longer an exported field (browser#923).
Last but not least, this release also includes constraints on browser contexts usage as now only one browserContext
is allowed per iteration, which means that the user can create a new browserContext
, close it, and then create it again; but can not have more than one "live" browserContext
. Instead, scenarios should be used to separate independent actions in a test (browser#929, browser#945).
With all these changes, a simple browser test now looks like:
import { browser } from 'k6/experimental/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium', // chromium is the only supported browser type so as long as
// the option is set, Chromium/Google Chrome will be used
},
},
},
},
};
export default async function () {
const page = browser.newPage();
try {
await page.goto('https://grafana.com')
} finally {
page.close();
}
}
Deprecations
- #3121 deprecates the
loadimpact/k6
docker image. We recommend switching to thegrafana/k6
image as soon as possible.
New features
Loki log output sending additional headers #3227
k6 has been able to send logs to Loki for nearly 3 years since v0.28.0 but didn't support any way to authenticate.
Now, it can be configured to send additional headers on every request.
This can be done by using the new header
config option, similar to label
:
k6 --log-output=loki=http://example.org,header.X-My-Header=123,header.Authorization=mytoken ...
The example above will now send the header X-My-Header
with the value 123
and the Authorization
header with the value mytoken
.
Thanks to @federicotdn for adding this feature.
Cloud Traces #3100, #3202
This release supports the new integration between k6 and Tempo in the cloud. Grafana Cloud k6 and Grafana Cloud Tempo customers will be able to start their traces in k6 using the existing k6/experimental/tracing
module to enrich their test run analysis page with metrics and aggregations from tracing data.
The new Cloud traces will work "out of the box" for k6 cloud
runs. In case of k6 run
execution, the K6_CLOUD_TRACES_ENABLED
environment variable has to be set to true
.
Ability to configure TLS per gRPC connection #3159, xk6-grpc#25
The k6/net/grpc
and k6/experimental/grpc
modules now support configuring TLS per-connection via ConnectParams. This is useful when connecting to a single gRPC server, using different TLS settings for each VUs, as well as when connecting to multiple gRPC servers from the same VU.
Expand to see an example of the new functionality.
import grpc from "k6/experimental/grpc";
import { check } from "k6";
import { SharedArray } from "k6/data";
import exec from "k6/execution";
// note: the services in this example don't exist. If you would like
// to run this example, make sure to replace the URLs, and
// the cacerts, cert, key, and password variables.
const grpcArgs = new SharedArray("grpc", () => {
// Using SharedArray here so that not every VU gets a copy of every certificate a key
return [
{
host: "foo1.grpcbin.test.k6.io:9001",
plaintext: false,
params: {
tls: {
cacerts: [open("cacerts0.pem")],
cert: open("cert0.pem"),
key: open("key0.pem"),
},
},
},
{
host: "foo2.grpcbin.test.k6.io:9002",
params: {
plaintext: false,
tls: {
cacerts: open("cacerts1.pem"),
cert: open("cert1.pem"),
key: open("key1.pem"),
password: "cert1-passphrase",
},
},
},
];
});
const client = new grpc.Client();
export default () => {
if (__ITER === 0) {
// Take one config and use it for this one VU
let grpcArg = grpcArgs[exec.vu.idInTest % grpcArgs.length];
client.connect(grpcArg.host, grpcArg.params);
}
const response = client.invoke("hello.HelloService/SayHello", {
greeting: "Bert",
});
check(response, {
"status is OK": (r) => r && r.status === grpc.StatusOK,
});
console.log(JSON.stringify(response.message));
};
Thanks @chrismoran-mica for the contribution 🙇♂️.
Cloud Output v2 #3117
After years of great service, we decided to refresh the k6 Cloud output introducing a more efficient end-to-end solution for ingesting the generated tests' metrics. The main change regards the protocol used for flushing metrics that is now a binary-based payload over HTTP.
The new output reduces the resources a load generator uses for tests that produce many metrics. There is no significant difference in the user experience; it's expected to be the same.
The one thing worth highlighting is that the new output is strict about tags, and it'll drops tags if they are reserved. For example:
- A custom tag named
test_run_id
, since that is reserved for internal k6 Cloud operations - Any tag with a key that starts with two underscores (
__
), that is marked by Prometheus convention as reserved
This is not yet the default Cloud output for the test runs executed from local machines (k6 run -o cloud
), but it is expected to be transparently enabled in the upcoming weeks.
The full list of related PRs: #3104, #3108, #3120, [#3125](https://github.com/grafana/k6/pul...
v0.45.1
k6 v0.45.1 is a patch release that fixes the build process for extensions:
- #3252, #3253 Due to the deletion of the go.buf.build registry that is used by the Prometheus remote write output, the building process for extensions has been broken.
There are no functional changes in k6 compared to v0.45.0.
v0.45.0
k6 v0.45.0 is here 🎉! This release includes:
- Experimental gRPC streaming support.
- Update scripts in the cloud without running tests.
- JS Metadata API.
- A lot of internal changes and bugfixes.
Breaking changes
- #3066 k6 will warn users about metric names incompatible with OpenTelemetry or Prometheus. This is planned to become an error with v0.48.0.
- browser#851 Remove existing browser namespaced metrics. These have been deprecated in favor of Web Vitals metrics.
New features
Experimental gRPC module with streaming support #3107
There is a new experimental module k6/experimental/grpc
. It is a copy of the k6/net/grpc
module with added stream support #2020.
Expand to see an example of the new functionality.
This example shows server streaming:
import { Client, Stream } from 'k6/experimental/grpc';
import { sleep } from 'k6';
const COORD_FACTOR = 1e7;
// to run this sample, you need to start the grpc server first.
// to start the grpc server, run the following command in k6 repository's root:
// go run -mod=mod examples/grpc_server/*.go
// (golang should be installed)
const GRPC_ADDR = __ENV.GRPC_ADDR || '127.0.0.1:10000';
const GRPC_PROTO_PATH = __ENV.GRPC_PROTO_PATH || '../../grpc_server/route_guide.proto';
let client = new Client();
client.load([], GRPC_PROTO_PATH);
export default () => {
client.connect(GRPC_ADDR, { plaintext: true });
const stream = new Stream(client, 'main.FeatureExplorer/ListFeatures', null);
stream.on('data', function (feature) {
console.log(
'Found feature called "' +
feature.name +
'" at ' +
feature.location.latitude / COORD_FACTOR +
', ' +
feature.location.longitude / COORD_FACTOR
);
});
stream.on('end', function () {
// The server has finished sending
client.close();
console.log('All done');
});
stream.on('error', function (e) {
// An error has occurred and the stream has been closed.
console.log('Error: ' + JSON.stringify(e));
});
// send a message to the server
stream.write({
lo: {
latitude: 400000000,
longitude: -750000000,
},
hi: {
latitude: 420000000,
longitude: -730000000,
},
});
sleep(0.5);
};
You can just replace k6/net/grpc
import with k6/experimental/grpc
to use the new functionality. Documentation for the module is available here.
In the future, this functionality will be moved to the k6/net/grpc
module.
You can now only upload a test to the cloud without running it #3030
For years users have wanted to be able to update the test that is saved in the cloud but not run it at this exact point.
This is now possible by adding --upload-only
when invoking k6 cloud
as in k6 cloud --upload-only script.js
.
This is likely going to be most useful in a CI on the actual test script project. Now that CI can just run k6 cloud --upload-only new-version-of-script.js
on "release".
And later on that newer version will be used. For example by a scheduled run.
Setting sample metadata API #3037
Support for high-cardinality metrics metadata was added in v0.41.0, but it wasn't accessible from test scripts. It's now possible to set or delete metadata for the whole VU with a similar API as used for tags:
import exec from "k6/execution";
export default () => {
exec.vu.metrics.metadata["my_cool_id"] = "a very unique value";
// all metrics from here on will have this metadata set
delete exec.vu.metrics.metadata["my_cool_id"];
// all metrics from here on will *not* have the metadata set
}
This also introduces the sub-object metrics
on the vu
object.
Apart from metadata
it has another property tags
. This is meant to be the new way to set tags instead of using exec.vu.tags
.
There are no current plans to replace exec.vu.tags
with exec.vu.metrics.tags
.
UX improvements and enhancements
- #3099 replace "breached" with "crossed" in logs around thresholds. Thanks to @MattDodsonEnglish 🙇.
- #3102 Better error message when SharedArray constructor is provided with an async function. This is not supported, but the original message wasn't very clear.
- #3089 Add Software Bill of Materials (SBOM) reports to k6 releases. Thanks to @SadFaceSmith 🙇.
- goja#510
JSON.parse
will now fail with a friendlier error message.
Bug fixes
- browser#852 Fix
Locator.WaitFor
fordetached
andhidden
states. - browser#859 Fix remote object parsing when subtype is
null
.
Maintenance and internal improvements
- #2991 Refactor JS modules system so that is usable in tests. Which allowed enabling the tc39 tests for modules #3040.
- #3025 Internally stop referring to afero and use an internal package to do all file system interaction. That package still uses afero.
- #3036 and #3053 Add options to
scenarios
for usage by browser module.
- #3058 fix repetitive
the
. Thank you, @cuishuang 🙇.
- #3064, #3070, #3075 and #3106 Go dependencies updates.
- #3067 Add method to retrieve all registered metrics.
- #3068 Add metric Sink constructor.
- #3078 Pin base Docker builder image to Alpine 3.17. Thank you, @arukiidou 🙇.
- #3086 Fix downloading
.golangci.yml
for PRs from forks. - #3088 Make TestDNSResolver less flaky.
- #3094 Fix example from the run command. Thanks to @rocktwotj 🙇.
- #3095 Maintenance update of
.golangci.yml
. - #3103 Fix lint and logical issues in
k6/data
module tests. - #3045, #3049, #3073 and #3044 New issues are now automatically assigned to maintainers, to improve response time on issues. Both new issue and new PR assignments are now not using external actions.
- #3109 Add a way to get the cloudapi Client's base URL. Thanks to @yorugac 🙇.
Roadmap
We're excited to share our public roadmap, outlining the upcoming features and improvements we have planned.
We hope this updated roadmap provides a clear overview of our plans for k6's future development. As always, we welcome feedback, corrections, and suggestions to make this roadmap more comprehensive, accessible, and valuable for the k6 community.
Cloud output v2
Work on a new version of the cloud output has been ongoing over this cycle.
While functionally it is now mostly complete, we feel like more testing is still needed and some smaller issues need to be ironed out.
Over the next cycle we will be testing it internally, and in v0.46.0 it will be generally available as the default Cloud output. It will still be possible to use the current version via an option, but we plan to gradually deprecate it.
The new output has some benefits over the previous one:
- Binary (protobuf) format instead of JSON #2963
- Samples aggregation for every metric instead of only for HTTP ones #3071
- HDR Histogram generation for trend-type metrics #3027
This in general makes the payload sent for tests with a lot of samples much smaller, which also in most cases has turned out to lower the CPU and memory usage.
Other related PRs: #3041, #3061, #3063, #3072, #3082, #3083, #3085, #3098, #3105
v0.44.1
k6 v0.44.1 is a patch release that fixes a couple of packaging issues:
- #3055 due to an oversight, the k6 package signing key in our RPM repository wasn't updated when its expiration date was extended in March.
- #3060 fixed building of Docker image due to a missing pinned
ca-certificates
version.
There are no functional changes in k6 compared to v0.44.0.
v0.44.0
k6 v0.44.0 is here! 🎉 This release includes:
- A new
k6/experimental/webcrypto
module implementing (partially) the Web Crypto API specification. - A sampling option for the experimental tracing module.
- Memory usage improvements.
- Bug fixes and UX improvements.
Some highlights from the k6/experimental/browser
module are:
locator.click
is now asynchronous, which is a breaking change.browserContext.addCookies
has now been implemented.browserType.Connect
has been implemented so k6 can now connect to an already running Chrome/Chromium browser instance.- Web vitals are natively supported when working with the browser module.
Breaking changes
The browser module is still in an experimental
stage, and therefore breaking changes are expected as we are improving the APIs to make them more user-friendly.
-
browser#790 Converted
locator.click
to async to have feature parity withpage.click
andelementHandle.click
. Users must remember to work withpromise.All
andpage.waitForNavigation()
when a click action results in navigation.A
locator.click
action that doesn't result in navigation can be used like so:const tails = page.locator("input[value='Bet on tails!']"); await tails.click(),
A
locator.click
action that does result in a navigation can be used like so:const tails = page.locator("input[value='Bet on tails!']"); await Promise.all([ page.waitForNavigation(), tails.click(), ]);
-
browser#817 We've removed
--no-sandbox
from the default Chrome launch arguments. Now Chrome will launch with a sandbox, which is a more secure way of running the browser. If you are running tests under aroot
user, the browser will no longer launch unless the--no-sandbox
argument is supplied. You can still pass this flag when launching a new Chrome instance using theargs
parameter onchromium.launch
:const browser = chromium.launch({ args: ['no-sandbox'], });
-
browser#844 Removed the exported
version
param from the root module. Users should from now on reference the k6 version instead of the browser module version. -
browser#838 Removed the first meaningful paint metric. This metric is being deprecated across all the browsers, because the metric's definition relies on browser-specific implementation details, and we've now introduced web vitals in the browser module which is a reliable industry standard way to measure frontend performance. You can find more details here.
-
browser#843 Removed the build step from Github Actions. From this release onwards, no new standalone browser binaries will be built and available from the releases section. The latest version of the browser module will be available in the k6 binary which can be found in the k6 releases page.
New features
A new k6/experimental/webcrypto
module implementing the Web Crypto API specification #3007
This release includes a new k6/experimental/webcrypto
module partially implementing the Web Crypto API specification in k6.
Expand to see an example of the new functionality.
This example shows encrypting and decrypting of a "Hello, World!" string using AES-CBC algorithm.
import { crypto } from 'k6/experimental/webcrypto';
export default async function () {
const key = await crypto.subtle.generateKey(
{
name: 'AES-CBC',
length: 256,
},
true,
['encrypt', 'decrypt']
);
const encoded = stringToArrayBuffer('Hello, World!');
const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = await crypto.subtle.encrypt(
{
name: 'AES-CBC',
iv: iv,
},
key,
encoded
);
const plaintext = await crypto.subtle.decrypt(
{
name: 'AES-CBC',
iv: iv,
},
key,
ciphertext
);
console.log(
'deciphered text == original text: ',
arrayBufferToHex(plaintext) === arrayBufferToHex(encoded)
);
}
function arrayBufferToHex(buffer) {
return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, '0')).join('');
}
function stringToArrayBuffer(str) {
var buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
You can see the list of currently supported APIs and algorithms in the project's README. Documentation for the module is available here.
Add sampling capabilities to the experimental tracing module #2886
This release adds sampling capabilities to the tracing module. You can now specify a sampling rate with the sampling
option when initializing a Client, or in the tracing.InstrumentHTTP
function.
browserContext.addCookies
browser#760
Cookies can now be added to a BrowserContext
and all new Page
s created from this context will have the cookie assigned to them. Thanks @zucchinho for implementing this feature!
const context = browser.newContext()
context.addCookies([{name: 'myCookie', value: 'hello world', url: 'https://test.k6.io'}])
const page = context.newPage()
browserType.Connect
browser#800
There are cases where the user may want to connect to a remote browser instance where they have more control over the browser lifecycle, such as when working in a resource bound environment. This feature enables users to connect to a manually started Chrome/Chromium browser instance. It's a simple case of replacing browser.launch
with browser.connect
and supplying the CDP url as the first argument. Not all launch
options will work with connect
since the browser instance should already have started prior to working with connect
. Since we assume that the user had decided to take ownership of starting the browser, we have made browser.close
a NOOP when working with browser.connect
, so the user will need to close the browser themselves.
const browser = chromium.connect('ws://127.0.0.1:1234/devtools/browser/e3bb7e53-ad0f-46f3-ae89-a8416868f4ce')
const page = browser.newPage();
Web Vitals are now natively supported by the browser module browser#836 browser#847
Web vitals are the defacto way for developers to measure their frontend performance using the core metrics:
These measurements are now calculated for all tests without any additional work from your side. Simply run your test as you have been doing and you will be presented with the new metrics in the output. This is the output after running the examples/fillform.js script:
webvital_cumulative_layout_shift..........: avg=0 min=0 med=0 max=0 p(90)=0 p(95)=0
webvital_cumulative_layout_shift_good.....: 1 0.323332/s
webvital_first_contentful_paint...........: avg=278.86ms min=141.1ms med=229.39ms max=466.1ms p(90)=418.76ms p(95)=442.43ms
webvital_first_contentful_paint_good......: 3 0.969995/s
webvital_first_input_delay................: avg=300µs min=200µs med=300µs max=399.99µs p(90)=379.99µs p(95)=389.99µs
webvital_first_input_delay_good...........: 2 0.646663/s
webvital_interaction_to_next_paint........: avg=16ms min=16ms med=16ms max=16ms p(90)=16ms p(95)=16ms
webvital_interaction_to_next_paint_good...: 1 0.323332/s
webvital_largest_content_paint............: avg=303.6ms min=141.1ms med=303.6ms max=466.1ms p(90)=433.6ms p(95)=449.85ms
webvital_largest_content_paint_good.......: 2 0.646663/s
webvital_time_to_first_byte...............: avg=205.23ms min=104.79ms med=188.39ms max=322.5ms p(90)=295.67ms p(95)=309.08ms
webvital_time_to_first_byte_good..........: 3 0.969995/s
You may have noticed other metrics in there too. We rely on the web-vitals JS library which exposes a few more metrics, so we've left them in for you to experiment with. You can find more details on all the browser module metrics in our documentation.
You will no longer see browser_first_contentful_paint
in the summary, and instead you can work with webvital_first_contentful_paint
.
UX improvements and enhancements
v0.43.1
k6 v0.43.1 is a patch release containing a few bugfixes:
- #2926 fixed a panic in
setup()
code whenvu.iterationInScenario
fromk6/execution
was used. - #2934 fixed a wrongly printed internal output ID to the
stdout
UI. - #2938 fixed a synchronization bug that caused k6 to get stuck after the end-of-test summary when sending the usage report took more than 3s. Thanks for reporting this, @ichasepucks!
v0.43.0
k6 v0.43.0 is here! 🎉
Notable changes in this release include:
- xk6-browser is now bundled in k6 as an experimental module, and usable without a separate binary or compilation step!
- Native support for JavaScript's
async
/await
. - A new experimental module for distributed tracing.
- Large refactoring of core components to simplify the code base, improve maintainability, and increase test coverage.
- Bug fixes, UX improvements, and maintenance.
Keep reading for the details.
Breaking changes
-
#2807 Use non-zero exit codes for tests aborted by Ctrl+C or the REST API.
Aborting a test run with Ctrl+C will now exit with code
105
, and stopping via the REST API will exit with code103
.
New Features
xk6-browser is now a built-in module #2884
This release includes xk6-browser as an experimental module. This means you can now also use the main k6 binary for browser automation and end-to-end testing, without needing to build a custom binary with xk6.
All xk6-browser scripts that work with v0.8.0 will continue to work with the built-in module in k6 v0.43.0. To use them, change the import path from k6/x/browser
to k6/experimental/browser
, and set the environment variable K6_BROWSER_ENABLED
to true
. The requirement to specify the environment variable is temporary and may be removed in a future k6 release. It was added to minimize the risks with k6 unexpectedly launching a browser (or another process) from k6 scripts. It's also a mechanism we use in the k6 Cloud, where browser tests are currently disabled.
For details, review the script example, or the updated browser module documentation.
The module is currently under the experimental
namespace, which means we reserve the decision to introduce breaking changes in the future. However, our mid-term goal is to drop the experimental
label and make browser support a stable part of the k6 feature set, eventually enabling it in k6 Cloud as well.
Native support for JavaScript's async
/await
#2830
In v0.35.0 we added support for asynchronous functionality in k6 scripts with the addition of Promise
.
While useful, the experience wasn't very friendly. Scripts had to use the .then()
API to chain Promises, instead of the await
syntax available in most other JavaScript runtimes, and the async
keyword wasn't supported. Some workarounds were possible, but it required a separate build pipeline to transpile the syntax into the older ES5.1+ standard supported by k6.
That is, until now! 🎉 With invaluable help from @dop251, who maintains goja, the JS VM k6 uses, v0.43.0 brings native async
/await
to your k6 scripts. This functionality works just as you'd expect in other JS runtimes, and makes working with async APIs much more convenient. For details, review the following http.asyncRequest()
example.
One caveat to note: async functions can't be passed to group()
or check()
. These functions are incompatible with asynchronous behavior, so you will get an error if trying to do so.
Experimental JavaScript module for distributed tracing #2853 #2854 #2855
This release brings a new experimental JavaScript module that adds distributed tracing support to k6. With one call in init
context, you can instrument your test script's HTTP requests. If the system you're testing is instrumented in the same way, this module brings visibility to SUT behavior for the lifetime of each request.
An example:
import tracing from 'k6/experimental/tracing';
import http from 'k6/http';
tracing.instrumentHTTP({
propagator: 'w3c',
});
export default () => {
http.get('https://httpbin.test.k6.io/get', {
headers: {
'X-Example-Header': 'instrumented/get',
},
});
};
For details and examples, refer to the tracing module documentation.
http.asyncRequest
#2877
The k6/http
module has a new asyncRequest
function that takes the same arguments as http.request()
, but returns a Promise
that, when used with await
, will be resolved with a Response
object. This gives you more control over script execution, as potentially the most time-consuming calls—making HTTP requests—will no longer block the thread of execution.
An example issuing a POST request:
import http from 'k6/http';
export default async function () {
const resPromise = http.asyncRequest(
'POST', 'https://httpbin.test.k6.io/post', { name: 'Bert' });
// Do something else here, make other requests, etc.
// Then when you're ready to use the response:
const resp = await resPromise;
console.log(resp.json().form.name); // Bert
}
This is one of the first steps towards migrating our APIs to be asynchronous, and similar changes can be expected in the future.
You can read more about asyncRequest
in the documentation.
Enhancements and UX improvements
- #2754, #2805 The output of the
k6 version
command has been enhanced to also show the version of all extensions built into the k6 binary produced by xk6. Thanks, @HarrisChu! - #2800 Improved handling of the Ctrl+C signal to gracefully abort the test during VU initialization.
- #2803 Ensure the REST API server is shut down after the test ends.
- #2867 Added the ability to display test run details and logs from k6 Cloud.
- #2890 Added a method for JavaScript modules to lookup environment variables without directly accessing the
os
package. - #2910 Added a bit more context when parsing the script options, so that it is more obvious what fails.
Bug fixes
- #2829 The
csv
output now correctly showsvu
anditer
system tags. This fixes a regression introduced in v0.40.0. Thanks, @leonyork! - #2851 Calling
k6/execution.test.abort()
within agroup()
now correctly exits the k6 process with code108
. Thanks for reporting this, @pomeh! - #2896 Fixed a panic in
k6/ws
when usingSocket.setInterval()
with values between 0 and 1. - #2903 Fixed a regression introduced in v0.42.0 where k6 will load the wrong module if the same import specifier has already been loaded, but they are pointing to different absolute paths, based on the files they are imported from.
- #2907 Fixed the exit code and
run_status
value when thecloud
output aborts a test.
Maintenance and internal improvements
- #2809, #2810, #2812, #2813, #2815, #2885, #2893 A core component of test execution, the
Engine
, was removed, and the behavior heavily refactored. This change simplifies the code base and unblocks further improvements. - #2821, #2864 Our high-level integration test suite was refactored and expanded, increasing the coverage of behavior that closely replicates real-world usage.
- #2803 Enabled checking for goroutine leaks.
- #2883 The goja runtime has been updated.
- #2845 Lint fixes in the
k6/http
module. - #2831 Compatibility with TC39 error messages was improved.
- #2846 The initialization of
js.Bundle
was simplified. - #2861, #2870 Some deprecated features and dependencies in our CI pipeline were removed.
- #2882 Our Dockerfile was improved with some linter suggestions. Thanks, @kempsterc!
Full Changelog: v0.42.0...v0.43.0