<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Grafana k6 release notes on Grafana Labs</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/</link><description>Recent content in Grafana k6 release notes on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/k6/v2.3.x/release-notes/index.xml" rel="self" type="application/rss+xml"/><item><title>Version 2.3.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.3.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.3.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-230-release-notes&#34;&gt;Version 2.3.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v2.3.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;--scenario&lt;/code&gt; flag to run selected parts of a test without editing the script.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;--once&lt;/code&gt; flag to reuse load-test scripts for smoke and functional testing.&lt;/li&gt;
&lt;li&gt;Experimental async &lt;code&gt;group()&lt;/code&gt; support that keeps metrics in the right group across &lt;code&gt;await&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Built-in byte encoding, Set operations, and raw JSON values.&lt;/li&gt;
&lt;li&gt;Opt-in fetching of missing TLS certificates for servers that work in browsers but fail in k6.&lt;/li&gt;
&lt;li&gt;Static labels for Prometheus remote write, nanosecond log timestamps, and WebSocket ready-state constants.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;run-selected-scenarios-6360&#34;&gt;Run selected scenarios &lt;a href=&#34;https://github.com/grafana/k6/pull/6360&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6360&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a script tests several services or user journeys, you may only need to run the part you are working on. The new &lt;code&gt;--scenario&lt;/code&gt; flag lets you select named scenarios without editing the script, adding environment-variable selection code, or splitting it into separate scripts. This addresses the long-standing &lt;a href=&#34;https://github.com/grafana/k6/issues/2780&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;request to run a subset of scenarios&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, this script defines two workloads:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;

export const options = {
  scenarios: {
    homepage: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      exec: &amp;#39;homepage&amp;#39;,
      vus: 2,
      iterations: 10,
    },
    contacts: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      exec: &amp;#39;contacts&amp;#39;,
      vus: 1,
      iterations: 5,
    },
  },
};

export function homepage() {
  http.get(&amp;#39;https://test.k6.io/&amp;#39;);
}

export function contacts() {
  http.get(&amp;#39;https://test.k6.io/contacts.php&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Run only the homepage workload, keeping its two VUs and ten total iterations:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 run --scenario homepage script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Select multiple scenarios with comma-separated names:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 run --scenario homepage,contacts script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Each selected scenario keeps its executor, load, timing, function, environment, tags, and browser options. Without the flag, k6 runs all configured scenarios. Selection also works with &lt;code&gt;k6 cloud run&lt;/code&gt; and &lt;code&gt;k6 archive&lt;/code&gt;; archives retain the selected configuration.&lt;/p&gt;
&lt;p&gt;Names must match configured scenarios. Load shortcuts such as &lt;code&gt;--vus&lt;/code&gt; and &lt;code&gt;--duration&lt;/code&gt; cannot be combined with selection; set the workload in each scenario instead. k6 warns and skips thresholds tagged for configured scenarios you exclude, while keeping global thresholds and other filters. See the &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/#run-selected-scenarios&#34;&gt;scenario documentation&lt;/a&gt; for details.&lt;/p&gt;
&lt;h3 id=&#34;run-a-script-once-with---once-6338&#34;&gt;Run a script once with &lt;code&gt;--once&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/6338&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6338&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--once&lt;/code&gt; flag is now the recommended way to run a script once, for both protocol and browser tests, while preserving the scenario&amp;rsquo;s function and browser configuration. It supports scripts with at most one scenario and works with &lt;code&gt;k6 run&lt;/code&gt;, &lt;code&gt;k6 cloud run&lt;/code&gt;, and &lt;code&gt;k6 archive&lt;/code&gt;. For example, this configuration runs &lt;code&gt;checkout()&lt;/code&gt; repeatedly with ten VUs for 30 seconds:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;export const options = {
  scenarios: {
    checkout: {
      executor: &amp;#39;constant-vus&amp;#39;,
      exec: &amp;#39;checkout&amp;#39;,
      vus: 10,
      duration: &amp;#39;30s&amp;#39;,
    },
  },
};
// The rest of the script defines checkout().&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 run --once script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With &lt;code&gt;--once&lt;/code&gt;, k6 changes this scenario to &lt;code&gt;shared-iterations&lt;/code&gt; with one VU and one iteration, calling &lt;code&gt;checkout()&lt;/code&gt; once instead of repeatedly for 30 seconds. Previously, shortcuts such as &lt;code&gt;--vus 1 --iterations 1&lt;/code&gt; replaced the script&amp;rsquo;s scenarios with a default scenario, discarding settings such as the selected function and browser options. This broke browser scripts because the configuration needed to launch Chromium was missing; &lt;code&gt;--once&lt;/code&gt; keeps that configuration. To combine scenario selection with &lt;code&gt;--once&lt;/code&gt;, see &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/#run-each-selected-scenario-once&#34;&gt;Run each selected scenario once&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;keep-metrics-grouped-across-asynchronous-calls-6340-6341-6342-6343-6344&#34;&gt;Keep metrics grouped across asynchronous calls &lt;a href=&#34;https://github.com/grafana/k6/pull/6340&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6340&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6341&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6341&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6342&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6342&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6343&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6343&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6344&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6344&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can now use an async function in &lt;code&gt;group()&lt;/code&gt; by enabling the experimental &lt;code&gt;async-metric-context&lt;/code&gt; feature. Requests and checks after an &lt;code&gt;await&lt;/code&gt; keep their group, and &lt;code&gt;group_duration&lt;/code&gt; measures until the callback&amp;rsquo;s returned promise settles. Previously, &lt;code&gt;group()&lt;/code&gt; rejected async functions, and promise callbacks could lose the group tag.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import { check, group } from &amp;#39;k6&amp;#39;;
import http from &amp;#39;k6/http&amp;#39;;

export default async function () {
  await group(&amp;#39;browse&amp;#39;, async () =&amp;gt; {
    const response = await http.asyncRequest(&amp;#39;GET&amp;#39;, &amp;#39;https://test.k6.io/&amp;#39;);
    check(response, { &amp;#39;page loaded&amp;#39;: (r) =&amp;gt; r.status === 200 });
    await http.asyncRequest(&amp;#39;GET&amp;#39;, &amp;#39;https://test.k6.io/contacts.php&amp;#39;);
  });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 run --features async-metric-context script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Both requests and the check belong to &lt;code&gt;browse&lt;/code&gt;, including the work after the first &lt;code&gt;await&lt;/code&gt;. The feature also preserves custom tags and metadata across promises, timer callbacks, &lt;code&gt;k6/websockets&lt;/code&gt; listeners, and gRPC stream listeners. Changes inside a group or callback stay local to that work and its asynchronous descendants instead of leaking into unrelated work.&lt;/p&gt;
&lt;h3 id=&#34;fetch-missing-tls-certificates-6137&#34;&gt;Fetch missing TLS certificates &lt;a href=&#34;https://github.com/grafana/k6/pull/6137&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6137&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some HTTPS servers work in browsers but fail in k6 because they omit an intermediate certificate. The new &lt;code&gt;tlsAIAFetch&lt;/code&gt; option lets k6 fetch that missing certificate while still verifying the server&amp;rsquo;s identity:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;export const options = {
  tlsAIAFetch: true,
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is opt-in and works with HTTP and gRPC connections. Thanks, @vtorosyan!&lt;/p&gt;
&lt;h3 id=&#34;static-labels-for-prometheus-remote-write-6071&#34;&gt;Static labels for Prometheus remote write &lt;a href=&#34;https://github.com/grafana/k6/pull/6071&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6071&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When several k6 instances send metrics to the same Prometheus server, labels let you tell their results apart. Use &lt;code&gt;K6_PROMETHEUS_RW_LABELS&lt;/code&gt; to identify the job, environment, or server on every time series sent by that output:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;K6_PROMETHEUS_RW_LABELS=&amp;#34;environment=production,server=srv1&amp;#34; \
k6 run --out experimental-prometheus-rw script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thanks, @rohan-patnaik!&lt;/p&gt;
&lt;h3 id=&#34;nanosecond-log-timestamps-6310&#34;&gt;Nanosecond log timestamps &lt;a href=&#34;https://github.com/grafana/k6/pull/6310&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6310&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Logs with second-precision timestamps can lose their order when a log service sorts messages emitted within the same second. Enable nanosecond timestamps with &lt;code&gt;--log-ns-timestamps&lt;/code&gt; to make those messages easier to order and correlate:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 --log-ns-timestamps --log-format=json run script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This also works with plain-text logs when &lt;code&gt;--no-color&lt;/code&gt; is set.&lt;/p&gt;
&lt;h3 id=&#34;websocket-ready-state-constants-6306&#34;&gt;WebSocket ready-state constants &lt;a href=&#34;https://github.com/grafana/k6/pull/6306&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6306&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;WebSocket&lt;/code&gt; constructor and its instances now expose the same connection-state constants as browsers: &lt;code&gt;CONNECTING&lt;/code&gt;, &lt;code&gt;OPEN&lt;/code&gt;, &lt;code&gt;CLOSING&lt;/code&gt;, and &lt;code&gt;CLOSED&lt;/code&gt;. For an existing socket, you can check its state before sending a message:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;if (socket.readyState === WebSocket.OPEN) {
  socket.send(&amp;#39;hello&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;encode-bytes-and-compare-sets-with-javascript-built-ins-6382&#34;&gt;Encode bytes and compare sets with JavaScript built-ins &lt;a href=&#34;https://github.com/grafana/k6/pull/6382&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6382&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can now convert &lt;code&gt;Uint8Array&lt;/code&gt; data to and from hex or base64 with built-in methods, and compare sets with operations such as &lt;code&gt;difference()&lt;/code&gt;, &lt;code&gt;intersection()&lt;/code&gt;, and &lt;code&gt;union()&lt;/code&gt;. Use them to prepare binary test data or check which fields are missing from a response.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const bytes = Uint8Array.fromHex(&amp;#39;6b36&amp;#39;);
console.log(bytes.toBase64()); // azY=

const expected = new Set([&amp;#39;id&amp;#39;, &amp;#39;name&amp;#39;, &amp;#39;email&amp;#39;]);
const received = new Set([&amp;#39;id&amp;#39;, &amp;#39;name&amp;#39;]);
console.log([...expected.difference(received)]); // [&amp;#34;email&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The Sobek update also adds &lt;code&gt;Error.isError()&lt;/code&gt;, &lt;code&gt;JSON.rawJSON()&lt;/code&gt;, and &lt;code&gt;JSON.isRawJSON()&lt;/code&gt;. For example, &lt;code&gt;JSON.rawJSON()&lt;/code&gt; lets you include an exact numeric value in a JSON payload without rounding it to a JavaScript &lt;code&gt;Number&lt;/code&gt; first:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;JSON.stringify({ id: JSON.rawJSON(&amp;#39;9007199254740993&amp;#39;) });
// &amp;#39;{&amp;#34;id&amp;#34;:9007199254740993}&amp;#39;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thanks to @arukiidou and @xtrafrancyz for the upstream implementations!&lt;/p&gt;
&lt;h3 id=&#34;read-the-execution-result-before-k6-exits-6388&#34;&gt;Read the execution result before k6 exits &lt;a href=&#34;https://github.com/grafana/k6/pull/6388&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6388&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When k6 stays alive with &lt;code&gt;--linger&lt;/code&gt;, tools monitoring the process cannot use its exit status to tell whether the test finished successfully or aborted. The REST API&amp;rsquo;s &lt;code&gt;/v1/status&lt;/code&gt; response now includes &lt;code&gt;execution_result&lt;/code&gt;, with the test&amp;rsquo;s exit code once it is known. Before then, the field is &lt;code&gt;null&lt;/code&gt;. Thanks, @yorugac!&lt;/p&gt;
&lt;p&gt;For example, query a lingering process after a script calls &lt;code&gt;exec.test.abort()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;curl -s http://localhost:6565/v1/status | jq &amp;#39;.data.attributes.execution_result&amp;#39;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JSON&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-json&#34;&gt;{
  &amp;#34;exit_code&amp;#34;: 108
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;ux-improvements-and-enhancements&#34;&gt;UX improvements and enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6408&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6408&lt;/a&gt; Explains how to enable experimental async support when &lt;code&gt;group()&lt;/code&gt; rejects an async callback.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6402&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6402&lt;/a&gt; Fixes a missing space in the &lt;code&gt;--no-usage-report&lt;/code&gt; help text. Thanks, @yats0x7!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6339&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6339&lt;/a&gt; Adds login and token-configuration guidance when Grafana Cloud commands return an authentication error. Thanks, @Swapnil-Biswas!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6414&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6414&lt;/a&gt; Stops screenshot capture from waiting until the page closes when a browser command does not respond, so scripts can catch the timeout and continue cleanup.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6412&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6412&lt;/a&gt; Lets waits for hidden or detached browser elements finish when the element is already absent, instead of timing out or throwing an error.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6368&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6368&lt;/a&gt; Makes &lt;code&gt;crypto.getRandomValues()&lt;/code&gt; throw a catchable &lt;code&gt;TypeError&lt;/code&gt; instead of crashing k6 when called without an argument or with a typed array whose length was overridden to a negative value. Thanks, @hyuraku!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6327&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6327&lt;/a&gt; Fixes a data race when closing a browser context while other browser operations access it. Thanks, @JohnPei1!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6351&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6351&lt;/a&gt; Prevents clearing an expired timer from running a later timeout or interval too early, and fixes VUs hanging when a &lt;code&gt;k6/websockets&lt;/code&gt; connection is closed before its handshake finishes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6349&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6349&lt;/a&gt; Fixes inflated &lt;code&gt;http_req_sending&lt;/code&gt; and &lt;code&gt;http_req_duration&lt;/code&gt; values when making HTTPS requests through an HTTPS proxy. Thanks, @kausthubhk!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5922&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5922&lt;/a&gt; Stops the test with an error when a ramping-VU scenario cannot start a VU, instead of silently stopping the scenario and reporting success. Thanks, @HwangRock!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6163&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6163&lt;/a&gt; Preserves Web Vitals from intermediate pages when a browser test navigates several times in the same tab, so results include those pages as well as the last one.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6355&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6355&lt;/a&gt; Prevents a response-body leak when reading a digest authentication challenge fails. Thanks, @cuishuang!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5949&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5949&lt;/a&gt; Supports sending metrics to InfluxDB behind a reverse proxy with a URL path prefix, such as &lt;code&gt;https://host/influxdb/database&lt;/code&gt;. Thanks, @o6ivp!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6235&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6235&lt;/a&gt; Uses forward slashes in remote screenshot paths on Windows. Thanks, @adarshsm!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6238&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6238&lt;/a&gt; Sends &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; form fields as empty values instead of the string &lt;code&gt;&amp;lt;nil&amp;gt;&lt;/code&gt;, and warns when a nested object cannot be encoded as a form field. Thanks, @djedi-knight!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6279&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6279&lt;/a&gt; Fixes browser tests stalling when VUs share a remote Chrome instance, so pages can run concurrently.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6298&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6298&lt;/a&gt; Preserves browser trace spans that were lost when a test ended. Thanks, @mem!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6385&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6385&lt;/a&gt; Fixes an integer overflow that prevented compilation on 32-bit ARM and x86 systems. Thanks, @GourangaDasSamrat!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-internal-improvements&#34;&gt;Maintenance and internal improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6413&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6413&lt;/a&gt; Updates the browser role-selector test fixture for Chromium&amp;rsquo;s image-map rendering.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6369&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6369&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6370&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6370&lt;/a&gt; Adds the binary&amp;rsquo;s build origin and a locally stored random installation ID to usage reports, helping distinguish build sources and measure active installations. Both respect &lt;code&gt;--no-usage-report&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6209&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6209&lt;/a&gt; Simplifies the internal handling of Cloud commands. Thanks, @moko-poi!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6236&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6236&lt;/a&gt; Adds a smoke test before publishing browser Docker images to catch missing or broken Chromium installations.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6245&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6245&lt;/a&gt; Enables CI on the v1 maintenance branch.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6246&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6246&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6277&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6277&lt;/a&gt; Automates approvals and merging for eligible dependency updates.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6256&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6256&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6271&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6271&lt;/a&gt; Improves contributor acknowledgments and the release checklist.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6270&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6270&lt;/a&gt; Restores Debian package publishing after &lt;code&gt;bzip2&lt;/code&gt; was removed from the packaging base image.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6301&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6301&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6373&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6373&lt;/a&gt; Corrects the TC39 test-package path and built-in module locations in contributor documentation. Thanks, @umekikazuya and @ashrafiucse!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6312&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6312&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6380&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6380&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6394&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6394&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6398&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6398&lt;/a&gt; Uses shared CI workflows, lint configuration, and Go test versions, and fixes findings from the newer linter.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6409&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6409&lt;/a&gt; Updates Test262 expectations for Unicode tests that pass with the newer Go version.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6325&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6325&lt;/a&gt; Moves browser option parsing into the JavaScript mapping layer without changing script behavior. Thanks, @Shobhit-Nagpal!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6265&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6265&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6266&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6266&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6267&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6267&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6303&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6303&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6304&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6304&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6331&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6331&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6332&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6332&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6357&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6357&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6358&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6358&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6363&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6363&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6367&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6367&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6383&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6383&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6376&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6376&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6478&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6478&lt;/a&gt; Updates the Docker build image to Go 1.27.1, raises the module&amp;rsquo;s minimum Go version to 1.26.0, and updates gRPC to v1.84.0 (the example server uses v1.83.2), OpenTelemetry to v1.46.0, Brotli to v1.2.3, &lt;code&gt;klauspost/compress&lt;/code&gt; to v1.20.0, esbuild to v0.28.2, Testify to v1.12.1, Protobuf to v1.36.12, &lt;code&gt;golang.org/x/crypto&lt;/code&gt; to v0.56.0, and related Go dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;external-contributors&#34;&gt;External contributors&lt;/h2&gt;
&lt;p&gt;Thanks to @adarshsm, @arukiidou, @ashrafiucse, @cuishuang, @djedi-knight, @GourangaDasSamrat, @HwangRock, @hyuraku, @JohnPei1, @kausthubhk, @mem, @moko-poi, @o6ivp, @rohan-patnaik, @Shobhit-Nagpal, @Swapnil-Biswas, @umekikazuya, @vtorosyan, @xtrafrancyz, @yats0x7, and @yorugac for their contributions.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-230-release-notes">Version 2.3.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v2.3.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>A &lt;code>--scenario&lt;/code> flag to run selected parts of a test without editing the script.&lt;/li>
&lt;li>A &lt;code>--once&lt;/code> flag to reuse load-test scripts for smoke and functional testing.&lt;/li>
&lt;li>Experimental async &lt;code>group()&lt;/code> support that keeps metrics in the right group across &lt;code>await&lt;/code>.&lt;/li>
&lt;li>Built-in byte encoding, Set operations, and raw JSON values.&lt;/li>
&lt;li>Opt-in fetching of missing TLS certificates for servers that work in browsers but fail in k6.&lt;/li>
&lt;li>Static labels for Prometheus remote write, nanosecond log timestamps, and WebSocket ready-state constants.&lt;/li>
&lt;/ul>
&lt;h2 id="new-features">New features&lt;/h2>
&lt;h3 id="run-selected-scenarios-6360">Run selected scenarios &lt;a href="https://github.com/grafana/k6/pull/6360" target="_blank" rel="noopener noreferrer">#6360&lt;/a>&lt;/h3>
&lt;p>When a script tests several services or user journeys, you may only need to run the part you are working on. The new &lt;code>--scenario&lt;/code> flag lets you select named scenarios without editing the script, adding environment-variable selection code, or splitting it into separate scripts. This addresses the long-standing &lt;a href="https://github.com/grafana/k6/issues/2780" target="_blank" rel="noopener noreferrer">request to run a subset of scenarios&lt;/a>.&lt;/p></description></item><item><title>Version 2.2.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.2.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.2.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-220-release-notes&#34;&gt;Version 2.2.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v2.2.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; now streams k6&amp;rsquo;s logs to Grafana Cloud, so the test run&amp;rsquo;s log view works for local execution too.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chromium.connectOverCDP()&lt;/code&gt;, which connects browser tests to an already-running Chromium instance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TextEncoder&lt;/code&gt; and &lt;code&gt;TextDecoder&lt;/code&gt; available as globals, and &lt;code&gt;WritableStream&lt;/code&gt; support in &lt;code&gt;k6/experimental/streams&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;k6 cloud load-zone list&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;Two new experimental feature flags: &lt;code&gt;merge-run-tags&lt;/code&gt; and &lt;code&gt;freeze-env&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;There are no breaking changes in this release.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;k6-cloud-run---local-execution-streams-logs-to-grafana-cloud-6171&#34;&gt;&lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; streams logs to Grafana Cloud &lt;a href=&#34;https://github.com/grafana/k6/pull/6171&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6171&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a cloud test runs locally with &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;, k6&amp;rsquo;s logs now stream to the Grafana Cloud test run, so the run&amp;rsquo;s log view is populated the same way it is for cloud execution. Use the new &lt;code&gt;--no-cloud-logs&lt;/code&gt; flag to opt out of streaming logs when working with &lt;code&gt;--local-execution&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud run --local-execution script.js
k6 cloud run --local-execution --no-cloud-logs script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;connect-to-a-running-browser-with-chromiumconnectovercdp-6165&#34;&gt;Connect to a running browser with &lt;code&gt;chromium.connectOverCDP()&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/6165&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6165&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/&#34;&gt;browser module&lt;/a&gt; can now attach to an existing Chromium-based browser over the Chrome DevTools Protocol, mirroring Playwright&amp;rsquo;s &lt;code&gt;browserType.connectOverCDP()&lt;/code&gt;. Pass the browser&amp;rsquo;s WebSocket endpoint and k6 manages the returned browser&amp;rsquo;s connection — it&amp;rsquo;s auto-closed at the end of the iteration, though you can call &lt;code&gt;close()&lt;/code&gt; earlier to release the connection on demand.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import { chromium } from &amp;#39;k6/browser&amp;#39;;

export default async function () {
  const browser = await chromium.connectOverCDP(&amp;#39;ws://localhost:9222/devtools/browser/&amp;lt;id&amp;gt;&amp;#39;);
  const page = await browser.newPage();

  try {
    await page.goto(&amp;#39;https://quickpizza.grafana.com/&amp;#39;);
  } finally {
    await page.close();
    await browser.close();
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Unlike the &lt;code&gt;K6_BROWSER_WS_URL&lt;/code&gt; environment variable, the endpoint is a runtime value — you can, for example, request a fresh session URL from a browser provider&amp;rsquo;s API in &lt;code&gt;setup()&lt;/code&gt; and connect to it from the iterations.&lt;/p&gt;
&lt;h3 id=&#34;textencoder-and-textdecoder-globals-6182&#34;&gt;&lt;code&gt;TextEncoder&lt;/code&gt; and &lt;code&gt;TextDecoder&lt;/code&gt; globals &lt;a href=&#34;https://github.com/grafana/k6/pull/6182&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6182&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;TextEncoder&lt;/code&gt; and &lt;code&gt;TextDecoder&lt;/code&gt; are now available as standard globals in both the init and VU contexts, no import required — matching how they are exposed in browsers and other JavaScript runtimes.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;const encoded = new TextEncoder().encode(&amp;#39;Hello, world!&amp;#39;);
const decoded = new TextDecoder().decode(encoded);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;writablestream-in-k6experimentalstreams-6132&#34;&gt;&lt;code&gt;WritableStream&lt;/code&gt; in &lt;code&gt;k6/experimental/streams&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/6132&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6132&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The experimental &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/streams/&#34;&gt;streams module&lt;/a&gt; now implements &lt;code&gt;WritableStream&lt;/code&gt; and &lt;code&gt;WritableStreamDefaultWriter&lt;/code&gt; following the WHATWG Streams specification, complementing the existing &lt;code&gt;ReadableStream&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import { WritableStream } from &amp;#39;k6/experimental/streams&amp;#39;;

export default async function () {
  const stream = new WritableStream({
    write(chunk) {
      console.log(`wrote ${chunk}`);
    },
  });

  const writer = stream.getWriter();
  await writer.write(&amp;#39;hello&amp;#39;);
  await writer.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;k6-cloud-load-zone-list-command-6142&#34;&gt;&lt;code&gt;k6 cloud load-zone list&lt;/code&gt; command &lt;a href=&#34;https://github.com/grafana/k6/pull/6142&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6142&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new &lt;code&gt;k6 cloud load-zone list&lt;/code&gt; subcommand lists the load zones — public and private — available in the configured Grafana Cloud k6 stack, mirroring the existing &lt;code&gt;k6 cloud project list&lt;/code&gt; command. Output defaults to a human-readable table; pass &lt;code&gt;--json&lt;/code&gt; to emit a JSON array instead.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;console&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-console&#34;&gt;$ k6 cloud load-zone list
Load zones for https://example.grafana.net:

ID                     NAME                     TYPE      AVAILABLE
amazon:us:ashburn      Ashburn, US (Amazon)     public    yes
amazon:sa:cape town    Cape Town, SA (Amazon)   public    yes&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;configurable-handlesummary-timeout-5854&#34;&gt;Configurable &lt;code&gt;handleSummary()&lt;/code&gt; timeout &lt;a href=&#34;https://github.com/grafana/k6/pull/5854&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5854&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The time budget for the &lt;code&gt;handleSummary()&lt;/code&gt; callback — previously hardcoded to 120 seconds — is now &lt;a href=&#34;/docs/k6/latest/using-k6/k6-options/reference/#handle-summary-timeout&#34;&gt;configurable&lt;/a&gt; through the &lt;code&gt;handleSummaryTimeout&lt;/code&gt; option or the &lt;code&gt;K6_HANDLE_SUMMARY_TIMEOUT&lt;/code&gt; environment variable, so long-running tests with heavy summaries no longer fail with &lt;code&gt;handleSummary() execution timed out&lt;/code&gt;. Thanks, @LBaronceli!&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;export const options = {
  handleSummaryTimeout: &amp;#39;5m&amp;#39;,
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;new-experimental-feature-flags-merge-run-tags-and-freeze-env&#34;&gt;New experimental feature flags: &lt;code&gt;merge-run-tags&lt;/code&gt; and &lt;code&gt;freeze-env&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Two new &lt;a href=&#34;/docs/k6/latest/using-k6/feature-flags/&#34;&gt;experimental feature flags&lt;/a&gt; join the feature-flag system introduced in v2.1.0:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5714&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5714&lt;/a&gt; &lt;code&gt;merge-run-tags&lt;/code&gt; merges run tags per key across config layers, so &lt;code&gt;options.tags&lt;/code&gt; in a script is no longer silently discarded when &lt;code&gt;--tag&lt;/code&gt; or &lt;code&gt;K6_TAGS&lt;/code&gt; is also used. Thanks, @yordis!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6032&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6032&lt;/a&gt; &lt;code&gt;freeze-env&lt;/code&gt; freezes the &lt;code&gt;__ENV&lt;/code&gt; object, so modifications from script code throw a &lt;code&gt;TypeError&lt;/code&gt; (in strict mode) instead of silently persisting across iterations and scenarios. Thanks, @lohitkolluri!&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 run --features merge-run-tags,freeze-env script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to the &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v2.2.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-220-release-notes">Version 2.2.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v2.2.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>&lt;code>k6 cloud run --local-execution&lt;/code> now streams k6&amp;rsquo;s logs to Grafana Cloud, so the test run&amp;rsquo;s log view works for local execution too.&lt;/li>
&lt;li>&lt;code>chromium.connectOverCDP()&lt;/code>, which connects browser tests to an already-running Chromium instance.&lt;/li>
&lt;li>&lt;code>TextEncoder&lt;/code> and &lt;code>TextDecoder&lt;/code> available as globals, and &lt;code>WritableStream&lt;/code> support in &lt;code>k6/experimental/streams&lt;/code>.&lt;/li>
&lt;li>A &lt;code>k6 cloud load-zone list&lt;/code> command.&lt;/li>
&lt;li>Two new experimental feature flags: &lt;code>merge-run-tags&lt;/code> and &lt;code>freeze-env&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>There are no breaking changes in this release.&lt;/p></description></item><item><title>Version 1.7.1 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.7.1/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.7.1/</guid><content><![CDATA[&lt;h1 id=&#34;version-171-release-notes&#34;&gt;Version 1.7.1 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 v1.7.1 is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dependency updates for &lt;code&gt;google.golang.org/grpc&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-internal-improvements&#34;&gt;Maintenance and internal improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5746&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5746&lt;/a&gt; Updates &lt;code&gt;google.golang.org/grpc&lt;/code&gt; which contains a fix for CVE-2026-33186.&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="version-171-release-notes">Version 1.7.1 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 v1.7.1 is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Dependency updates for &lt;code>google.golang.org/grpc&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="maintenance-and-internal-improvements">Maintenance and internal improvements&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/5746" target="_blank" rel="noopener noreferrer">#5746&lt;/a> Updates &lt;code>google.golang.org/grpc&lt;/code> which contains a fix for CVE-2026-33186.&lt;/li>
&lt;/ul></description></item><item><title>Version 1.7.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.7.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.7.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-170-release-notes&#34;&gt;Version 1.7.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 v1.7.0 is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic resolution for subcommand extensions — no more manual &lt;code&gt;xk6&lt;/code&gt; builds required to use them!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;K6_SECRET_SOURCE&lt;/code&gt; env var as an alternative to &lt;code&gt;--secret-source&lt;/code&gt;, accepting the same syntax.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;There are no breaking changes in this release.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;automatic-resolution-for-subcommand-extensions-5664&#34;&gt;Automatic resolution for subcommand extensions &lt;a href=&#34;https://github.com/grafana/k6/pull/5664&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5664&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can now rely on &lt;a href=&#34;/docs/k6/latest/extensions/run/#use-automatic-extension-resolution&#34;&gt;automatic extension resolution&lt;/a&gt;
also when using a &lt;a href=&#34;/docs/k6/latest/extensions/create/subcommand-extensions/&#34;&gt;subcommand extension&lt;/a&gt;
that isn&amp;rsquo;t included in the current binary.&lt;/p&gt;
&lt;p&gt;Previously, using extension subcommands required manually building a custom &lt;code&gt;k6&lt;/code&gt; binary with &lt;code&gt;xk6&lt;/code&gt;. Now, k6 detects the
missing extension, provisions the binary on demand, and executes the command transparently — the same experience already
available for JavaScript extensions.&lt;/p&gt;
&lt;p&gt;For instance, if you run:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 x httpbin&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;and the &lt;a href=&#34;https://github.com/grafana/xk6-subcommand-httpbin&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;xk6-subcommand-httpbin&lt;/code&gt;&lt;/a&gt; subcommand extension isn&amp;rsquo;t in the
current binary, k6 will automatically provision a binary with it on demand and execute the command transparently.&lt;/p&gt;
&lt;h2 id=&#34;roadmap&#34;&gt;Roadmap&lt;/h2&gt;
&lt;p&gt;k6 &lt;code&gt;v2.0.0&lt;/code&gt; is the next planned release. It will include a set of breaking changes that have been discussed and planned.
You can find the list of planned breaking changes in &lt;a href=&#34;https://github.com/grafana/k6/issues/5062&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5062&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-170-release-notes">Version 1.7.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 v1.7.0 is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Automatic resolution for subcommand extensions — no more manual &lt;code>xk6&lt;/code> builds required to use them!&lt;/li>
&lt;li>&lt;code>K6_SECRET_SOURCE&lt;/code> env var as an alternative to &lt;code>--secret-source&lt;/code>, accepting the same syntax.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>There are no breaking changes in this release.&lt;/p></description></item><item><title>Version 1.6.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.6.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.6.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-160-release-notes&#34;&gt;Version 1.6.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 v1.6.0 is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud commands now support configurable default Grafana Cloud stack.&lt;/li&gt;
&lt;li&gt;New &lt;code&gt;k6 deps&lt;/code&gt; command for analyzing script dependencies.&lt;/li&gt;
&lt;li&gt;Browser APIs enhancements with &lt;code&gt;frameLocator()&lt;/code&gt;, &lt;code&gt;goBack()&lt;/code&gt;, &lt;code&gt;goForward()&lt;/code&gt; methods.&lt;/li&gt;
&lt;li&gt;Crypto module adds PBKDF2 support for password-based key derivation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jslib&lt;/code&gt; gets a new TOTP library for time-based one-time password generation and validation.&lt;/li&gt;
&lt;li&gt;New &lt;a href=&#34;https://github.com/grafana/mcp-k6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;mcp-k6&lt;/a&gt; MCP server for AI-assisted k6 script writing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;There are no breaking changes in this release.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;configurable-default-stack-for-cloud-commands-5420&#34;&gt;Configurable default stack for Cloud commands &lt;a href=&#34;https://github.com/grafana/k6/pull/5420&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5420&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cloud commands now support configuring the default Grafana Cloud stack you want to use. The stack slug (or stack id) is used by the Cloud to determine which default project to use when not explicitly provided.&lt;/p&gt;
&lt;p&gt;Previously, users had to specify the project id for every test run. With this change, you can configure a default stack during login, and k6 will use it to automatically resolve the appropriate default project. This is particularly useful for organizations with multiple Grafana Cloud stacks or when working across different teams and environments.&lt;/p&gt;
&lt;p&gt;Users can also set up a specific stack for every test run, either using the new option &lt;code&gt;stackID&lt;/code&gt; or the environment variable &lt;code&gt;K6_CLOUD_STACK_ID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Please note that, in k6 v2, this stack information will become &lt;strong&gt;mandatory&lt;/strong&gt; to run a test.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# Login interactively and select default stack
k6 cloud login

# Login and set default stack with token
k6 cloud login --token $MY_TOKEN --stack my-stack-slug

# Run test using the configured default stack
k6 cloud run script.js

# Run test using a specific stack
K6_CLOUD_STACK_ID=12345 k6 cloud run script.js

# Stack id can also be set in the options
export const options = {
  cloud: {
    stackID: 123,
    projectID: 789,  // If the project does not belong to the stack, this will throw an error
  },
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This simplifies the cloud testing workflow and prepares k6 for upcoming changes to the Grafana Cloud k6 authentication process, where the stack will eventually become mandatory.&lt;/p&gt;
&lt;h3 id=&#34;k6-deps-command-and-manifest-support-5410-5427&#34;&gt;&lt;code&gt;k6 deps&lt;/code&gt; command and manifest support &lt;a href=&#34;https://github.com/grafana/k6/pull/5410&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5410&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5427&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5427&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new &lt;code&gt;k6 deps&lt;/code&gt; command is now available to analyze and list all dependencies of a given script or archive. This is particularly useful for understanding which extensions are required to run a script, especially when using auto extension resolution.&lt;/p&gt;
&lt;p&gt;The command identifies all imports in your script and lists dependencies that might be needed for building a new binary with auto extension resolution. Like auto extension resolution itself, this only accounts for imports, not dynamic &lt;code&gt;require()&lt;/code&gt; calls.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# Analyze script dependencies
k6 deps script.js

# Output in JSON format for programmatic consumption
k6 deps --json script.js

# Analyze archived test dependencies
k6 deps archive.tar&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This makes it easier to understand extension requirements, share scripts with clear dependency information, and integrate k6 into automated build pipelines.&lt;/p&gt;
&lt;p&gt;In addition, k6 now supports a manifest that specifies default version constraints for dependencies when no version is defined in the script using pragmas. If a dependency is imported without an explicit version, it defaults to &amp;ldquo;*&amp;rdquo;, and the manifest can be used to replace that with a concrete version constraint.&lt;/p&gt;
&lt;p&gt;The manifest is set through an environment variable as JSON with keys being a dependency and values being constraints:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;K6_DEPENDENCIES_MANIFEST=&amp;#39;{&amp;#34;k6/x/faker&amp;#34;: &amp;#34;&amp;gt;=v0.4.4&amp;#34;}&amp;#39; k6 run scripts.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, if the script only imports &lt;code&gt;k6/x/faker&lt;/code&gt; and does not use a &lt;code&gt;use k6 with k6/x/faker ...&lt;/code&gt; directive, it will set the version constraint to &lt;code&gt;&amp;gt;=v0.4.4&lt;/code&gt;. It will not make any changes if &lt;code&gt;k6/x/faker&lt;/code&gt; is not a dependency of the script at all.&lt;/p&gt;
&lt;h3 id=&#34;browser-module-framelocator-method-5487&#34;&gt;Browser module: &lt;code&gt;frameLocator()&lt;/code&gt; method &lt;a href=&#34;https://github.com/grafana/k6/pull/5487&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5487&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/framelocator/&#34;&gt;&lt;code&gt;frameLocator()&lt;/code&gt;&lt;/a&gt; on &lt;code&gt;Page&lt;/code&gt;, &lt;code&gt;Frame&lt;/code&gt;, &lt;code&gt;Locator&lt;/code&gt;, and &lt;code&gt;FrameLocator&lt;/code&gt; objects. This method creates a locator for working with &lt;code&gt;iframe&lt;/code&gt; elements without the need to explicitly switch contexts, making it much easier to interact with embedded content.&lt;/p&gt;
&lt;p&gt;Frame locators are particularly valuable when testing applications with nested iframes, as they allow you to chain locators naturally while maintaining readability:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to expand example code&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  try {
    await page.goto(&amp;#39;https://example.com&amp;#39;);

    // Locate an iframe and interact with elements inside it
    const frame = page.frameLocator(&amp;#39;#payment-iframe&amp;#39;);
    await frame.locator(&amp;#39;#card-number&amp;#39;).fill(&amp;#39;4242424242424242&amp;#39;);
    await frame.locator(&amp;#39;#submit-button&amp;#39;).click();

    // Chain frame locators for nested iframes
    const nestedFrame = page
      .frameLocator(&amp;#39;#outer-frame&amp;#39;)
      .frameLocator(&amp;#39;#inner-frame&amp;#39;);
    await nestedFrame.locator(&amp;#39;#nested-content&amp;#39;).click();
  } finally {
    await page.close();
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;This complements existing frame handling methods and provides a more intuitive API for working with iframe-heavy applications.&lt;/p&gt;
&lt;h3 id=&#34;browser-module-goback-and-goforward-navigation-methods-5494&#34;&gt;Browser module: &lt;code&gt;goBack()&lt;/code&gt; and &lt;code&gt;goForward()&lt;/code&gt; navigation methods &lt;a href=&#34;https://github.com/grafana/k6/pull/5494&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5494&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/goback/&#34;&gt;&lt;code&gt;page.goBack()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/goforward/&#34;&gt;&lt;code&gt;page.goForward()&lt;/code&gt;&lt;/a&gt; methods for browser history navigation. These methods allow you to navigate the page&amp;rsquo;s history, similar to clicking the browser&amp;rsquo;s back/forward buttons.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to expand example code&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  try {
    await page.goto(&amp;#39;https://example.com&amp;#39;);
    await page.goto(&amp;#39;https://example.com/page2&amp;#39;);
    
    // Navigate back to the previous page
    await page.goBack();
    
    // Navigate forward again
    await page.goForward();
    
    // Both methods support optional timeout and waitUntil parameters
    await page.goBack({ waitUntil: &amp;#39;networkidle&amp;#39; });
  } finally {
    await page.close();
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&#34;browser-module-request-event-handlers-5481-5486&#34;&gt;Browser module: Request event handlers &lt;a href=&#34;https://github.com/grafana/k6/pull/5481&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5481&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5486&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5486&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;code&gt;page.on(&#39;requestfailed&#39;)&lt;/code&gt; and &lt;code&gt;page.on(&#39;requestfinished&#39;)&lt;/code&gt; event handlers, enabling better monitoring and debugging of network activity during browser tests.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;requestfailed&lt;/code&gt; event fires when a request fails (network errors, aborts, etc.), while &lt;code&gt;requestfinished&lt;/code&gt; fires when a request completes successfully.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to expand example code&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  // Monitor failed requests
  page.on(&amp;#39;requestfailed&amp;#39;, (request) =&amp;gt; {
    console.log(`Request failed: ${request.url()}`);
  });

  // Monitor successful requests
  page.on(&amp;#39;requestfinished&amp;#39;, (request) =&amp;gt; {
    console.log(`Request finished: ${request.url()}`);
  });

  await page.goto(&amp;#39;https://example.com&amp;#39;);
  await page.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;These event handlers provide deeper insights into network behavior during browser testing and help identify issues that might not be immediately visible.&lt;/p&gt;
&lt;h3 id=&#34;crypto-module-pbkdf2-support-5380&#34;&gt;Crypto module: PBKDF2 support &lt;a href=&#34;https://github.com/grafana/k6/pull/5380&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5380&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The crypto module now supports PBKDF2 for deriving cryptographic keys from passwords. PBKDF2 is widely used for password hashing and key derivation in security-sensitive applications, and this addition enables testing of systems that use PBKDF2 for authentication or encryption.&lt;/p&gt;
&lt;p&gt;For usage examples, check out the &lt;a href=&#34;https://github.com/grafana/k6/blob/b7c19129a8dc5a69bb519e7b313eab6473114e6f/examples/webcrypto/derive_key/derive-key-pbkdf2.js&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;one provided&lt;/a&gt; in the repository or refer to the &lt;a href=&#34;/docs/k6/latest/javascript-api/crypto/pbkdf2params/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;websockets-module-is-now-stable-5586&#34;&gt;WebSockets module is now stable &lt;a href=&#34;https://github.com/grafana/k6/pull/5586&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5586&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The websockets module has been promoted to stable status and is now available via the &lt;code&gt;k6/websockets&lt;/code&gt; path.
The experimental &lt;code&gt;k6/experimental/websockets&lt;/code&gt; module will be removed in a future release. Users should migrate to the stable &lt;code&gt;k6/websockets&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;To migrate, simply update your import statement:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Old (experimental)
import ws from &amp;#39;k6/experimental/websockets&amp;#39;;

// New (stable)
import ws from &amp;#39;k6/websockets&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;No other changes are required because the API is the same.&lt;/p&gt;
&lt;h3 id=&#34;console-logging-arraybuffer-and-typedarray-support-5496&#34;&gt;Console logging: ArrayBuffer and TypedArray support &lt;a href=&#34;https://github.com/grafana/k6/pull/5496&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5496&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;console.log()&lt;/code&gt; now properly displays &lt;code&gt;ArrayBuffer&lt;/code&gt; and &lt;code&gt;TypedArray&lt;/code&gt; objects, making it easier to debug binary data handling in your test scripts. Previously, these types would not display useful information, making debugging difficult when working with binary protocols, file uploads, or WebSocket binary messages.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to expand example code&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Log ArrayBuffer - shows detailed byte contents
const buffer = new ArrayBuffer(8);
const view = new Int32Array(buffer);
view[0] = 4;
view[1] = 2;
console.log(buffer);
// Output: ArrayBuffer { [Uint8Contents]: &amp;lt;04 00 00 00 02 00 00 00&amp;gt;, byteLength: 8 }

// Log TypedArrays - shows type, length, and values
const int32 = new Int32Array([4, 2]);
console.log(int32);
// Output: Int32Array(2) [ 4, 2 ]

// Nested objects with TypedArrays
console.log({ v: int32 });
// Output: { v: Int32Array(2) [ 4, 2 ] }

// Complex nested structures
console.log({ 
  name: &amp;#34;test&amp;#34;, 
  buffer: buffer, 
  view: int32 
});
// Output: { name: &amp;#34;test&amp;#34;, buffer: ArrayBuffer {...}, view: Int32Array(2) [...] }&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&#34;configurable-tls-version-for-experimental-prometheus-output-5537&#34;&gt;Configurable TLS version for Experimental Prometheus output &lt;a href=&#34;https://github.com/grafana/k6/pull/5537&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5537&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The experimental Prometheus remote write output now supports configuring the minimum TLS version used for connections. This allows you to meet specific security requirements or compatibility constraints when sending metrics to Prometheus endpoints.&lt;/p&gt;
&lt;p&gt;If not set, the default minimum TLS version is 1.3.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;K6_PROMETHEUS_RW_TLS_MIN_VERSION=1.3 k6 run script.js -o experimental-prometheus-rw&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;a-new-totp-library-k6-totp&#34;&gt;A new TOTP library &lt;a href=&#34;https://github.com/grafana/k6-jslib-totp&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6-totp&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new TOTP (Time-based One-Time Password) library is now available in &lt;a href=&#34;https://jslib.k6.io&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;jslib.k6.io&lt;/a&gt;, enabling k6 scripts to generate and validate time-based one-time passwords. This is particularly useful for testing applications that use TOTP-based two-factor authentication (2FA), such as authenticator apps like Google Authenticator or Authy. See the documentation at &lt;a href=&#34;/docs/k6/latest/javascript-api/jslib/totp/&#34;&gt;k6-totp&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to expand example code&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;
import { TOTP } from &amp;#39;https://jslib.k6.io/totp/1.0.0/index.js&amp;#39;;

export default async function () {
  // Initialize TOTP with your secret key (base32 encoded)
  // The second parameter is the number of digits (typically 6 or 8)
  const totp = new TOTP(&amp;#39;GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&amp;#39;, 6);

  // Generate the current TOTP code
  const code = await totp.gen();
  console.log(`Generated TOTP code: ${code}`);

  // Use the TOTP code in authentication
  const response = http.post(&amp;#39;https://api.example.com/login&amp;#39;, JSON.stringify({
    username: &amp;#39;user@example.com&amp;#39;,
    password: &amp;#39;password123&amp;#39;,
    totpCode: code
  }), {
    headers: { &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json&amp;#39; }
  });

  // Optionally verify a TOTP code
  const isValid = await totp.verify(code);
  console.log(`Code is valid: ${isValid}`);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;The library follows &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6238&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;the standard TOTP RFC 6238 specification&lt;/a&gt;, ensuring compatibility with standard authenticator applications.&lt;/p&gt;
&lt;h3 id=&#34;introducing-mcp-k6-ai-assisted-k6-script-writing-mcp-k6&#34;&gt;Introducing mcp-k6: AI-assisted k6 script writing &lt;a href=&#34;https://github.com/grafana/mcp-k6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;mcp-k6&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/mcp-k6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;mcp-k6&lt;/a&gt; is an experimental &lt;a href=&#34;https://modelcontextprotocol.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Model Context Protocol&lt;/a&gt; (MCP) server for k6. Once connected to your AI assistant or MCP-compatible editor (such as Cursor, VS Code, or Claude Desktop), it helps you write better k6 scripts faster and run them with confidence.&lt;/p&gt;
&lt;p&gt;With mcp-k6, your AI assistant can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Write accurate scripts:&lt;/strong&gt; Create up-to-date scripts by referring to embedded k6 documentation and TypeScript definitions to reduce API hallucinations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validate scripts:&lt;/strong&gt; Catch syntax errors, missing imports, and &lt;code&gt;export default function&lt;/code&gt; declarations before execution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run tests locally:&lt;/strong&gt; Execute scripts and review results without leaving your editor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generate scripts:&lt;/strong&gt; Create tests from requirements using guided prompts that follow k6 best practices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Convert browser tests:&lt;/strong&gt; Transform Playwright tests into k6 browser scripts while preserving test logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get started, install mcp-k6 via Docker, Homebrew, or from source, then configure it with your editor. See the &lt;a href=&#34;/docs/k6/latest/set-up/configure-ai-assistant/&#34;&gt;documentation&lt;/a&gt; for installation instructions and setup guides.&lt;/p&gt;
&lt;h2 id=&#34;roadmap&#34;&gt;Roadmap&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ve started looking into k6 v2! The major release will focus on introducing breaking changes that have accumulated throughout the v1.x series, such as removing deprecated APIs and changing default behaviors. New features will continue to be incrementally released in v1.x minor versions as usual until the v2.0.0 release.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-160-release-notes">Version 1.6.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 v1.6.0 is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Cloud commands now support configurable default Grafana Cloud stack.&lt;/li>
&lt;li>New &lt;code>k6 deps&lt;/code> command for analyzing script dependencies.&lt;/li>
&lt;li>Browser APIs enhancements with &lt;code>frameLocator()&lt;/code>, &lt;code>goBack()&lt;/code>, &lt;code>goForward()&lt;/code> methods.&lt;/li>
&lt;li>Crypto module adds PBKDF2 support for password-based key derivation.&lt;/li>
&lt;li>&lt;code>jslib&lt;/code> gets a new TOTP library for time-based one-time password generation and validation.&lt;/li>
&lt;li>New &lt;a href="https://github.com/grafana/mcp-k6" target="_blank" rel="noopener noreferrer">mcp-k6&lt;/a> MCP server for AI-assisted k6 script writing.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>There are no breaking changes in this release.&lt;/p></description></item><item><title>Version 1.5.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.5.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.5.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-150-release-notes&#34;&gt;Version 1.5.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v1.5.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changes in the browser module:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;page.waitForEvent()&lt;/code&gt; for event-based synchronization with page events.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;locator.pressSequentially()&lt;/code&gt; for character-by-character typing simulation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Improved debugging with deep object logging in &lt;code&gt;console.log()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Extended WebSocket support with close code and reason information.&lt;/li&gt;
&lt;li&gt;Enhanced extension ecosystem with custom subcommands and DNS resolver access.&lt;/li&gt;
&lt;li&gt;URL-based secret management for external secret services.&lt;/li&gt;
&lt;li&gt;New machine-readable summary format for test results.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;As per our &lt;a href=&#34;/docs/k6/latest/reference/versioning-and-stability-guarantees/&#34;&gt;stability guarantees&lt;/a&gt;, breaking changes across minor releases are allowed only for experimental features.&lt;/p&gt;
&lt;h3 id=&#34;breaking-changes-for-experimental-modules&#34;&gt;Breaking changes for experimental modules&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5237&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5237&lt;/a&gt; Deprecates the &lt;code&gt;experimental/redis&lt;/code&gt; module. The module will be removed in a future release. Users should migrate to alternative solutions, such as &lt;a href=&#34;https://github.com/grafana/xk6-redis&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;the official k6 Redis extension&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;pagewaitforevent-5478&#34;&gt;&lt;code&gt;page.waitForEvent()&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5478&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5478&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/waitforevent/&#34;&gt;&lt;code&gt;page.waitForEvent()&lt;/code&gt;&lt;/a&gt;, which blocks the caller until a specified event is captured. If a predicate is provided, it waits for an event that satisfies the predicate. This method is particularly valuable for testing scenarios where you need to synchronize your test flow with specific browser or page events before proceeding with the next action.&lt;/p&gt;
&lt;p&gt;Event-driven synchronization is vital for test reliability, especially when dealing with asynchronous operations where timing is unpredictable. This is more robust than using fixed delays and helps avoid flaky tests.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  // Wait for a console message containing specific text
  const msgPromise = page.waitForEvent(&amp;#39;console&amp;#39;, msg =&amp;gt; msg.text().includes(&amp;#39;hello&amp;#39;));
  await page.evaluate(() =&amp;gt; console.log(&amp;#39;hello world&amp;#39;));
  const msg = await msgPromise;
  console.log(msg.text());
  // Output: hello world

  // Wait for a response from a specific URL with timeout
  const resPromise = page.waitForEvent(&amp;#39;response&amp;#39;, {
    predicate: res =&amp;gt; res.url().includes(&amp;#39;/api/data&amp;#39;),
    timeout: 5000,
  });
  await page.click(&amp;#39;button#fetch-data&amp;#39;);
  const res = await resPromise;

  await page.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;locatorpresssequentially-5457&#34;&gt;&lt;code&gt;locator.pressSequentially()&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5457&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5457&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/presssequentially/&#34;&gt;&lt;code&gt;locator.pressSequentially()&lt;/code&gt;&lt;/a&gt;, which types text character by character, firing keyboard events (&lt;code&gt;keydown&lt;/code&gt;, &lt;code&gt;keypress&lt;/code&gt;, &lt;code&gt;keyup&lt;/code&gt;) for each character. This method is essential for testing features that depend on gradual typing to trigger specific behaviors, such as autocomplete suggestions, real-time input validation per character, or dynamic character counters. Thank you, @rajan2345, for the contribution 🎉&lt;/p&gt;
&lt;p&gt;The method supports a configurable delay between keystrokes, enabling you to simulate realistic typing speeds and test time-dependent input handlers:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  try {
    await page.goto(&amp;#39;https://test.k6.io/browser.php&amp;#39;);

    // Type text character by character
    const searchInput = page.locator(&amp;#39;#text1&amp;#39;);
    await searchInput.pressSequentially(&amp;#39;Hello World&amp;#39;);

    // Type with delay to simulate realistic typing speed
    await searchInput.clear();
    await searchInput.pressSequentially(&amp;#39;test query&amp;#39;, { delay: 100 });
  } finally {
    await page.close();
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This complements existing text input methods: &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/fill/&#34;&gt;&lt;code&gt;locator.fill()&lt;/code&gt;&lt;/a&gt; for simple form filling, &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/type/&#34;&gt;&lt;code&gt;locator.type()&lt;/code&gt;&lt;/a&gt; for gradual typing without keyboard events, and now &lt;code&gt;pressSequentially&lt;/code&gt; for character-by-character typing with full keyboard event firing. Thank you, @rajan2345, for the contribution 🎉&lt;/p&gt;
&lt;h3 id=&#34;consolelog-deep-object-logging-5460&#34;&gt;&lt;code&gt;console.log()&lt;/code&gt; Deep Object Logging &lt;a href=&#34;https://github.com/grafana/k6/pull/5460&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5460&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;console.log()&lt;/code&gt; now properly traverses and displays complex JavaScript structures, including functions, classes, and circular references. Previously, Sobek&amp;rsquo;s JSON marshaling would lose nested functions, classes, and other non-serializable types, making debugging painful.&lt;/p&gt;
&lt;p&gt;Objects with mixed function and class properties are now properly displayed:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;console.log({
  one: class {},
  two: function() {}
});
// Before: {}
// After:  {&amp;#34;one&amp;#34;:&amp;#34;[object Function]&amp;#34;,&amp;#34;two&amp;#34;:&amp;#34;[object Function]&amp;#34;}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Nested arrays and objects with functions are now fully traversed:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;console.log([
  { handler: class {} },
  { data: [1, 2, class {}] }
]);
// Before: [{},{&amp;#34;data&amp;#34;:[1,2,null]}]
// After:  [{&amp;#34;handler&amp;#34;:&amp;#34;[object Function]&amp;#34;},{&amp;#34;data&amp;#34;:[1,2,&amp;#34;[object Function]&amp;#34;]}]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Complex objects with multiple property types are properly preserved:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;console.log({
  a: [1, 2, 3],
  b: class {},
  c: () =&amp;gt; {},
  d: function() {},
  e: [1, () =&amp;gt; {}, function() {}, class {}, 2]
});
// Before: {&amp;#34;a&amp;#34;:[1,2,3],&amp;#34;e&amp;#34;:[1,null,null,null,2]}
// After:  {
//   &amp;#34;a&amp;#34;:[1,2,3],
//   &amp;#34;b&amp;#34;:&amp;#34;[object Function]&amp;#34;,
//   &amp;#34;c&amp;#34;:&amp;#34;[object Function]&amp;#34;,
//   &amp;#34;d&amp;#34;:&amp;#34;[object Function]&amp;#34;,
//   &amp;#34;e&amp;#34;:[1,&amp;#34;[object Function]&amp;#34;,&amp;#34;[object Function]&amp;#34;,&amp;#34;[object Function]&amp;#34;,2]
// }&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Circular references are now properly detected and marked:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const obj = {
  fn: function() {},
  foo: {}
};
obj.foo = obj;

console.log(obj);
// Before: [object Object]
// After:  {&amp;#34;fn&amp;#34;:&amp;#34;[object Function]&amp;#34;,&amp;#34;foo&amp;#34;:&amp;#34;[Circular]&amp;#34;}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This improvement makes debugging k6 test scripts significantly easier when working with API responses, event handlers, and complex state objects.&lt;/p&gt;
&lt;h3 id=&#34;experimentalwebsockets---close-code-and-reason-support-5376&#34;&gt;&lt;code&gt;experimental/websockets&lt;/code&gt; - Close Code and Reason Support &lt;a href=&#34;https://github.com/grafana/k6/pull/5376&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5376&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The experimental WebSockets module now supports sending close codes and reasons when closing connections, and properly captures close event information. This is essential for testing WebSocket
implementations that rely on specific close codes to determine whether a connection was closed normally or due to an error.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import ws from &amp;#39;k6/experimental/websockets&amp;#39;;

export default function () {
  const socket = ws.connect(&amp;#39;ws://example.com&amp;#39;, (socket) =&amp;gt; {
    socket.on(&amp;#39;close&amp;#39;, (data) =&amp;gt; {
      console.log(`Connection closed with code: ${data.code}, reason: ${data.reason}`);
      // Output: Connection closed with code: 1000, reason: Normal closure
    });
  });

  // Close with code and reason
  socket.close(1000, &amp;#39;Normal closure&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thanks, @etodanik, for the contribution 🎉&lt;/p&gt;
&lt;h3 id=&#34;subcommand-extension-support-5399&#34;&gt;Subcommand Extension Support &lt;a href=&#34;https://github.com/grafana/k6/pull/5399&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5399&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Extensions can now register custom subcommands under the &lt;code&gt;k6 x&lt;/code&gt; namespace, enabling custom command-line tools that integrate seamlessly with k6. This provides a consistent and discoverable way for extensions to offer specialized CLI utilities while maintaining k6&amp;rsquo;s familiar command structure.&lt;/p&gt;
&lt;p&gt;Extensions can now define custom commands like:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;k6 x my-tool --help
k6 x debug --inspect&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This integration pattern allows extension authors to provide powerful tooling that feels native to the k6 ecosystem.&lt;/p&gt;
&lt;h3 id=&#34;dns-resolver-access-5421&#34;&gt;DNS Resolver Access &lt;a href=&#34;https://github.com/grafana/k6/pull/5421&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5421&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Extensions can now access k6&amp;rsquo;s DNS resolver for custom DNS handling and networking extensions. The resolver respects k6&amp;rsquo;s configuration including &lt;code&gt;hosts&lt;/code&gt; overrides, custom DNS servers, and DNS caching settings. This enables extensions to use it directly instead of having to reproduce the functionality. Which also makes them work the same way as native modules.&lt;/p&gt;
&lt;h3 id=&#34;machine-readable-summary-format-5338&#34;&gt;Machine-Readable Summary Format &lt;a href=&#34;https://github.com/grafana/k6/pull/5338&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5338&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new machine-readable summary format for the end-of-test summary is now available, providing structured, programmatic shapes via &lt;code&gt;--summary-export&lt;/code&gt; and &lt;code&gt;handleSummary()&lt;/code&gt;. This format is designed for easier integration with external systems and analytics pipelines.&lt;/p&gt;
&lt;p&gt;The new format is currently opt-in via the &lt;code&gt;--new-machine-readable-summary&lt;/code&gt; flag or &lt;code&gt;K6_NEW_MACHINE_READABLE_SUMMARY&lt;/code&gt; environment variable, and will become the default in k6 v2:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;k6 run script.js --new-machine-readable-summary --summary-export=summary.json&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This makes it easier to integrate k6 results into CI/CD pipelines, dashboards, and custom analysis tools.&lt;/p&gt;
&lt;h3 id=&#34;url-based-secret-management-5413&#34;&gt;URL-Based Secret Management &lt;a href=&#34;https://github.com/grafana/k6/pull/5413&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5413&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The secret management system now supports URL-based secret sources, allowing k6 to fetch secrets from HTTP endpoints. This lets users implement a simple HTTP API to provide secrets to a test. There is a mock implementation, but no particular production-ready implementation is provided at this time. In the future, there is potential for proxies to other systems, including HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault.&lt;/p&gt;
&lt;h2 id=&#34;roadmap&#34;&gt;Roadmap&lt;/h2&gt;
&lt;h3 id=&#34;deprecation-of-first-input-delay-fid-web-vital&#34;&gt;Deprecation of First Input Delay (FID) Web Vital&lt;/h3&gt;
&lt;p&gt;Following the official &lt;a href=&#34;https://web.dev/blog/fid&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;web vitals guidance&lt;/a&gt;, First Input Delay (FID) is no longer a Core Web Vital as of September 9, 2024, having been replaced by Interaction to Next Paint (INP). The k6 browser module already emits INP metrics, and we&amp;rsquo;re planning to deprecate FID support to align with industry standards.&lt;/p&gt;
&lt;p&gt;FID only measures the delay before the browser runs your event handler, so it ignores the time your code takes and the delay to paint the UI—often underestimating how slow an interaction feels. INP captures the full interaction latency (input delay &#43; processing &#43; next paint) across a page’s interactions, so it better reflects real user-perceived responsiveness and is replacing FID.&lt;/p&gt;
&lt;h4 id=&#34;action-required&#34;&gt;Action required&lt;/h4&gt;
&lt;p&gt;If you&amp;rsquo;re currently using FID in your test scripts for thresholds or relying on it in external integrations, you should migrate to using INP as soon as possible.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Instead of relying on FID
export const options = {
  thresholds: {
    // &amp;#39;browser_web_vital_fid&amp;#39;: [&amp;#39;p(95)&amp;lt;100&amp;#39;], // Deprecated
    &amp;#39;browser_web_vital_inp&amp;#39;: [&amp;#39;p(95)&amp;lt;200&amp;#39;], // Use INP instead
  },
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This change ensures k6 browser testing stays aligned with modern web performance best practices and Core Web Vitals standards.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to the &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v1.5.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-150-release-notes">Version 1.5.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v1.5.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Changes in the browser module:
&lt;ul>
&lt;li>&lt;code>page.waitForEvent()&lt;/code> for event-based synchronization with page events.&lt;/li>
&lt;li>&lt;code>locator.pressSequentially()&lt;/code> for character-by-character typing simulation.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Improved debugging with deep object logging in &lt;code>console.log()&lt;/code>.&lt;/li>
&lt;li>Extended WebSocket support with close code and reason information.&lt;/li>
&lt;li>Enhanced extension ecosystem with custom subcommands and DNS resolver access.&lt;/li>
&lt;li>URL-based secret management for external secret services.&lt;/li>
&lt;li>New machine-readable summary format for test results.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>As per our &lt;a href="/docs/k6/latest/reference/versioning-and-stability-guarantees/">stability guarantees&lt;/a>, breaking changes across minor releases are allowed only for experimental features.&lt;/p></description></item><item><title>Version 1.4.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.4.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.4.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-140-release-notes&#34;&gt;Version 1.4.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v1.4.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenTelemetry output graduated from experimental to stable status.&lt;/li&gt;
&lt;li&gt;Changes in the Browser module:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;page.waitForRequest&lt;/code&gt; for waiting on specific HTTP requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QueryAll&lt;/code&gt; methods now return elements in DOM order.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;locator.evaluate&lt;/code&gt; and &lt;code&gt;locator.evaluateHandle&lt;/code&gt; for executing JavaScript code in the page context with access to the matching element.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page.unroute(url)&lt;/code&gt; and &lt;code&gt;page.unrouteAll&lt;/code&gt; for removing routes registered with &lt;code&gt;page.route&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;As per our &lt;a href=&#34;/docs/k6/latest/reference/versioning-and-stability-guarantees/&#34;&gt;stability guarantees&lt;/a&gt;, breaking changes across minor releases are allowed only for experimental features.&lt;/p&gt;
&lt;h3 id=&#34;breaking-changes-for-experimental-modules&#34;&gt;Breaking changes for experimental modules&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5164&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5164&lt;/a&gt; OpenTelemetry output now exports rate metrics as a single counter with &lt;code&gt;zero&lt;/code&gt;/&lt;code&gt;nonzero&lt;/code&gt; labels instead of separate metrics.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5333&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5333&lt;/a&gt; OpenTelemetry output configuration: &lt;code&gt;K6_OTEL_EXPORTER_TYPE&lt;/code&gt; is deprecated in favor of &lt;code&gt;K6_OTEL_EXPORTER_PROTOCOL&lt;/code&gt; to align with OpenTelemetry standards.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;breaking-changes-for-undefined-behaviours&#34;&gt;Breaking changes for undefined behaviours&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5320&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5320&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5239&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5239&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5342&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5342&lt;/a&gt; Automatic extension resolution now only inspects ES module &lt;code&gt;import&lt;/code&gt; statements and no longer supports CommonJS &lt;code&gt;require()&lt;/code&gt; calls. CommonJS &lt;code&gt;require()&lt;/code&gt; calls are dynamic, and it is not possible to know for certain if they will be called, or if they will be called with static strings - the only way they were even previously loaded. This functionality was a quirk of the previous implementation and had numerous problems. Additionally, &lt;code&gt;use k6&lt;/code&gt; directives are now only recognized when they appear at the beginning of files (after optional shebang and whitespace/comments). This was the original intention, but due to implementation bugs, it did not accurately reflect what was happening.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;opentelemetry-output-graduation-5334&#34;&gt;OpenTelemetry output graduation &lt;a href=&#34;https://github.com/grafana/k6/pull/5334&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5334&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The OpenTelemetry output has graduated from experimental status and is now available as a stable output using the name &lt;code&gt;opentelemetry&lt;/code&gt;. This change makes OpenTelemetry the recommended vendor-agnostic solution for exporting k6 telemetry data.&lt;/p&gt;
&lt;p&gt;You can now use the stable output name in your k6 commands:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# Previous experimental usage (still supported for backward compatibility)
k6 run --out experimental-opentelemetry script.js

# New stable usage
k6 run --out opentelemetry script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;experimental-opentelemetry&lt;/code&gt; name will continue to work for backward compatibility for now but it&amp;rsquo;s deprecated and we might remove it in future versions. We recommend migrating to use the new &lt;code&gt;opentelemetry&lt;/code&gt; name.&lt;/p&gt;
&lt;h3 id=&#34;pagewaitforrequest-5330&#34;&gt;&lt;code&gt;page.waitForRequest&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5330&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5330&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/waitforrequest/&#34;&gt;&lt;code&gt;page.waitForRequest()&lt;/code&gt;&lt;/a&gt;, which allows you to wait for HTTP requests that match specific URL patterns during browser automation. This method is particularly valuable for testing scenarios where you need to ensure specific network requests are initiated before proceeding with test actions.&lt;/p&gt;
&lt;p&gt;The method supports multiple URL pattern matching strategies:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Wait for exact URL match
const requestPromise = page.waitForRequest(&amp;#39;https://api.example.com/data&amp;#39;);
await page.click(&amp;#39;button[data-testid=&amp;#34;load-data&amp;#34;]&amp;#39;);
const request = await requestPromise;

// Wait for regex pattern match
await page.waitForRequest(/\/api\/.*\.json$/);

// Use with Promise.all for coordinated actions
await Promise.all([
  page.waitForRequest(&amp;#39;https://api.example.com/user-data&amp;#39;),
  page.click(&amp;#39;button[data-testid=&amp;#34;load-user-data&amp;#34;]&amp;#39;)
]);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This complements the existing &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/waitforresponse/&#34;&gt;&lt;code&gt;page.waitForResponse()&lt;/code&gt;&lt;/a&gt; method by focusing on HTTP requests rather than responses, providing more granular control over network-dependent test scenarios.&lt;/p&gt;
&lt;h3 id=&#34;pageunrouteurl-and-pageunrouteall-5223&#34;&gt;&lt;code&gt;page.unroute(url)&lt;/code&gt; and &lt;code&gt;page.unrouteAll()&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5223&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5223&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/unroute/&#34;&gt;&lt;code&gt;page.unroute(url)&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/unrouteall/&#34;&gt;&lt;code&gt;page.unrouteAll()&lt;/code&gt;&lt;/a&gt;, allowing you to remove routes previously registered with &lt;code&gt;page.route&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;await page.route(/.*\/api\/pizza/, function (route) {
  console.log(&amp;#39;Modifying request to /api/pizza&amp;#39;);
  route.continue({
    postData: JSON.stringify({
      customName: &amp;#39;My Pizza&amp;#39;,
    }),
  });
});
...

await page.unroute(/.*\/api\/pizza/); // The URL needs to be exactly the same as the one used in the call to the `route` function&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;await page.route(/.*\/api\/pizza/, function (route) {
  console.log(&amp;#39;Modifying request to /api/pizza&amp;#39;);
  route.continue({
    postData: JSON.stringify({
      customName: &amp;#39;My Pizza&amp;#39;,
    }),
  });
});
...

await page.unrouteAll();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;locatorevaluate-and-locatorevaluatehandle-5306&#34;&gt;&lt;code&gt;locator.evaluate&lt;/code&gt; and &lt;code&gt;locator.evaluateHandle&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5306&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5306&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/evaluate/&#34;&gt;&lt;code&gt;locator.evaluate&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/evaluatehandle/&#34;&gt;&lt;code&gt;locator.evaluateHandle&lt;/code&gt;&lt;/a&gt;, allowing you to execute JavaScript code in the page context with access to the matching element. The only difference between &lt;code&gt;evaluate&lt;/code&gt; and &lt;code&gt;evaluateHandle&lt;/code&gt; is that &lt;code&gt;evaluateHandle&lt;/code&gt; returns a &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/jshandle/&#34;&gt;JSHandle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;await check(page, {
  &amp;#39;calling evaluate&amp;#39;: async p =&amp;gt; {
    const n = await p.locator(&amp;#39;#pizza-name&amp;#39;).evaluate(pizzaName =&amp;gt; pizzaName.textContent);
    return n == &amp;#39;Our recommendation:&amp;#39;;
  }
});

await check(page, {
  &amp;#39;calling evaluate with arguments&amp;#39;: async p =&amp;gt; {
    const n = await p.locator(&amp;#39;#pizza-name&amp;#39;).evaluate((pizzaName, extra) =&amp;gt; pizzaName.textContent &amp;#43; extra, &amp;#39; Super pizza!&amp;#39;);
    return n == &amp;#39;Our recommendation: Super pizza!&amp;#39;;
  }
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const jsHandle = await page.locator(&amp;#39;#pizza-name&amp;#39;).evaluateHandle((pizzaName) =&amp;gt; pizzaName);

const obj = await jsHandle.evaluateHandle((handle) =&amp;gt; {
  return { innerText: handle.innerText };
});
console.log(await obj.jsonValue()); // {&amp;#34;innerText&amp;#34;:&amp;#34;Our recommendation:&amp;#34;}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;new-officially-supported-k6-dns-extension&#34;&gt;New officially supported &lt;a href=&#34;https://github.com/grafana/xk6-dns&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6 DNS extension&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-x-dns/&#34;&gt;&lt;code&gt;xk6-dns&lt;/code&gt; extension&lt;/a&gt; is now officially supported in k6 OSS and k6 Cloud. You can import &lt;code&gt;k6/x/dns&lt;/code&gt; directly in your scripts thanks to &lt;a href=&#34;/docs/grafana-cloud/testing/k6/author-run/use-k6-extensions/&#34;&gt;automatic extension resolution&lt;/a&gt;, with no custom build required.&lt;/p&gt;
&lt;p&gt;Use it to perform DNS resolution testing as part of your tests: resolve names via custom or system DNS, measure resolution latency and errors, validate records before HTTP steps, compare resolvers, and even load test DNS servers in end‑to‑end scenarios.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import dns from &amp;#39;k6/x/dns&amp;#39;;

export default function () {
  const answer = dns.resolve(&amp;#39;grafana.com&amp;#39;, { recordType: &amp;#39;A&amp;#39; });
  console.log(answer.records.map(({ address }) =&amp;gt; address).join(&amp;#39;, &amp;#39;));
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The extension currently supports A and AAAA record lookups. If you would like to see additional record types supported, please consider &lt;a href=&#34;https://github.com/grafana/xk6-dns&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;contributing to the extension&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;automatic-extension-resolution-improvements-5320-5239-5342-5332-5240&#34;&gt;Automatic extension resolution improvements &lt;a href=&#34;https://github.com/grafana/k6/pull/5320&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5320&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5239&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5239&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5342&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5342&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5332&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5332&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5240&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5240&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automatic extension resolution has been completely reimplemented to use k6&amp;rsquo;s internal module loader instead of the external &lt;code&gt;k6deps&lt;/code&gt;/esbuild pipeline. This change brings significant improvements in reliability and maintainability.&lt;/p&gt;
&lt;p&gt;As part of the rewrite, a few issues and unintended &lt;em&gt;features&lt;/em&gt; were found, namely:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Trying to follow &lt;code&gt;require&lt;/code&gt; calls, which, due to their dynamic nature, don&amp;rsquo;t work particularly stably. That is, depending on where and how the &lt;code&gt;require&lt;/code&gt; call was used, k6 might decide whether it is needed or not. And it definitely doesn&amp;rsquo;t work when using actual string variables. Support for CommonJS is primarily for backward compatibility, so after an internal discussion, we opted not to support it at all. We could bring this back until v2, if there is enough interest. However, in the long term, it is intended that this not be part of k6.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;use k6 with &amp;hellip;&amp;rdquo; directives were parsed from the whole file instead of just the beginning, which leads to numerous problems, and was not the intended case. As such, they are now only parsed at the beginning of files (not just the main one) with potential empty lines and comments preceding them.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// main.js
&amp;#34;use k6 with k6/x/faker&amp;#34;
import { faker } from &amp;#39;k6/x/faker&amp;#39;;
import { helper } from &amp;#39;./utils.js&amp;#39;;

export default function() {
  console.log(faker.name());
  helper();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Or, an example using the directive with CommonJS&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// utils.js
&amp;#34;use k6 with k6/x/redis&amp;#34;
const redis = require(&amp;#39;k6/x/redis&amp;#39;);

exports.helper = function() {
  // Use redis extension
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, k6 will detect both &lt;code&gt;k6/x/faker&lt;/code&gt; and &lt;code&gt;k6/x/redis&lt;/code&gt; extensions from the &lt;code&gt;use k6&lt;/code&gt; directives in both files and provision a binary that includes both extensions if needed.&lt;/p&gt;
&lt;p&gt;Other fixes this brings are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Fixes for path related issues (irregardless of usage of the feature) on windows, especially between drives. It is possible there were problems on other OSes that were just not reported. &lt;a href=&#34;https://github.com/grafana/k6/issues/5176&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5176&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Syntax errors were not reported as such, as the underlying &lt;code&gt;esbuild&lt;/code&gt; parsing will fail, but won&amp;rsquo;t be handled well. &lt;a href=&#34;https://github.com/grafana/k6/issues/5127&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5127&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/issues/5104&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5104&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Propagating exit codes from a sub-process running the new k6. This lets you use the result of the exit code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to the &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v1.4.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-140-release-notes">Version 1.4.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v1.4.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>OpenTelemetry output graduated from experimental to stable status.&lt;/li>
&lt;li>Changes in the Browser module:
&lt;ul>
&lt;li>&lt;code>page.waitForRequest&lt;/code> for waiting on specific HTTP requests.&lt;/li>
&lt;li>&lt;code>QueryAll&lt;/code> methods now return elements in DOM order.&lt;/li>
&lt;li>&lt;code>locator.evaluate&lt;/code> and &lt;code>locator.evaluateHandle&lt;/code> for executing JavaScript code in the page context with access to the matching element.&lt;/li>
&lt;li>&lt;code>page.unroute(url)&lt;/code> and &lt;code>page.unrouteAll&lt;/code> for removing routes registered with &lt;code>page.route&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>As per our &lt;a href="/docs/k6/latest/reference/versioning-and-stability-guarantees/">stability guarantees&lt;/a>, breaking changes across minor releases are allowed only for experimental features.&lt;/p></description></item><item><title>Version 1.3.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.3.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.3.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-130-release-notes&#34;&gt;Version 1.3.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 v1.3.0 is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Browser module gets:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;locator.locator&lt;/code&gt;, &lt;code&gt;locator.contentFrame&lt;/code&gt;, and &lt;code&gt;FrameLocator.locator&lt;/code&gt; for powerful locator chaining and iframe handling.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;locator|frame|FrameLocator.getBy*&lt;/code&gt; for targeting elements without relying on brittle CSS selectors.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;locator.filter&lt;/code&gt; for filtering locators for more precise element targeting.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;locator.boundingBox&lt;/code&gt; for retrieving element geometry.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page.waitForResponse&lt;/code&gt; for waiting on specific HTTP responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;deprecations&#34;&gt;Deprecations&lt;/h2&gt;
&lt;h3 id=&#34;a-new-summary-mode-disabled-has-been-introduced-to-replace-the-no-summary-option-5118&#34;&gt;A new summary mode &lt;code&gt;disabled&lt;/code&gt; has been introduced to replace the &amp;ldquo;no summary&amp;rdquo; option &lt;a href=&#34;https://github.com/grafana/k6/pull/5118&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5118&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--no-summary&lt;/code&gt; flag and its corresponding environment variable &lt;code&gt;K6_NO_SUMMARY&lt;/code&gt; have been deprecated in favor of
the new &lt;code&gt;disabled&lt;/code&gt; summary mode. This change unifies the configuration experience for controlling the end-of-test summary.&lt;/p&gt;
&lt;p&gt;You can now disable the end-of-test summary with either &lt;code&gt;--summary-mode=disabled&lt;/code&gt; or &lt;code&gt;K6_SUMMARY_MODE=disabled&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;the-legacy-summary-mode-has-been-deprecated-5138&#34;&gt;The &lt;code&gt;legacy&lt;/code&gt; summary mode has been deprecated &lt;a href=&#34;https://github.com/grafana/k6/pull/5138&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5138&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;legacy&lt;/code&gt; summary mode was introduced in k6 v1.0, when the end-of-test summary was revamped with the addition of two
new modes: &lt;code&gt;compact&lt;/code&gt; and &lt;code&gt;full&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Its purpose was to ease the transition for users who relied heavily on the old summary format.
However, we’ve now reached the point where it’s time to deprecate it.&lt;/p&gt;
&lt;p&gt;The plan is to fully remove it in k6 v2.0, so please migrate to either &lt;code&gt;compact&lt;/code&gt; or &lt;code&gt;full&lt;/code&gt; to ensure readiness for the
next major release.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;locatorlocator-5073&#34;&gt;&lt;code&gt;locator.locator&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5073&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5073&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/locator/&#34;&gt;&lt;code&gt;locator.locator&lt;/code&gt;&lt;/a&gt; method allows you to define locators relative to a parent locator, enabling powerful locator chaining and nesting. This feature lets you create more precise element targeting by combining multiple selectors in a hierarchical manner.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;await page
  .locator(&amp;#39;[data-testid=&amp;#34;inventory&amp;#34;]&amp;#39;)
  .locator(&amp;#39;[data-item=&amp;#34;apples&amp;#34;]&amp;#39;)
  .locator(&amp;#39;button.add&amp;#39;)
  .click();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This nesting capability provides a more intuitive way to navigate complex DOM structures and serves as the foundation for other &lt;code&gt;locator&lt;/code&gt; APIs in this release that require such hierarchical targeting.&lt;/p&gt;
&lt;h3 id=&#34;locatorcontentframe-5075&#34;&gt;&lt;code&gt;locator.contentFrame&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5075&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5075&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/contentframe/&#34;&gt;&lt;code&gt;locator.contentFrame()&lt;/code&gt;&lt;/a&gt;, which returns a new type &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/framelocator/&#34;&gt;&lt;code&gt;frameLocator&lt;/code&gt;&lt;/a&gt;. This method is essential for switching context from the parent page to iframe contents.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;frameLocator&lt;/code&gt; types target iframe elements on the page and provide a gateway to interact with their contents. Unlike regular &lt;code&gt;locator&lt;/code&gt;s that work within the current &lt;code&gt;frame&lt;/code&gt; context, &lt;code&gt;frameLocator&lt;/code&gt;s specifically target iframe elements and prepare them for content interaction.&lt;/p&gt;
&lt;p&gt;This approach is essential for iframe interaction because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Iframes create separate DOM contexts that require special handling.&lt;/li&gt;
&lt;li&gt;Browsers enforce security boundaries between frames.&lt;/li&gt;
&lt;li&gt;Iframe content may load asynchronously and needs proper waiting.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;elementHandle&lt;/code&gt; for iframe interactions is error-prone and can lead to stale references, while &lt;code&gt;frameLocator&lt;/code&gt; provide reliable, auto-retrying approaches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Get iframe element and switch to its content frame
const iframeLocator = page.locator(&amp;#39;iframe[name=&amp;#34;payment-form&amp;#34;]&amp;#39;);
const frame = await iframeLocator.contentFrame();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;framelocatorlocator-5075&#34;&gt;&lt;code&gt;frameLocator.locator&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5075&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5075&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ve also added &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/framelocator/locator/&#34;&gt;&lt;code&gt;frameLocator.locator&lt;/code&gt;&lt;/a&gt; which allows you to create &lt;code&gt;locator&lt;/code&gt;s for elements inside an iframe. Once you&amp;rsquo;ve targeted an iframe with &lt;code&gt;page.contentFrame()&lt;/code&gt;, you can use &lt;code&gt;.locator()&lt;/code&gt; to find and interact with elements within that iframe&amp;rsquo;s content with the &lt;code&gt;frameLocator&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Target an iframe and interact with elements inside it
const iframe = page.locator(&amp;#39;iframe[name=&amp;#34;checkout-frame&amp;#34;]&amp;#39;).contentFrame();
await iframe.locator(&amp;#39;input[name=&amp;#34;card-number&amp;#34;]&amp;#39;).fill(&amp;#39;4111111111111111&amp;#39;);
await iframe.locator(&amp;#39;button[type=&amp;#34;submit&amp;#34;]&amp;#39;).click();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This functionality enables testing of complex web applications that use iframes for embedded content, payment processing, authentication widgets, and third-party integrations.&lt;/p&gt;
&lt;h3 id=&#34;locatorboundingbox-5076&#34;&gt;&lt;code&gt;locator.boundingBox&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5076&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5076&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/boundingbox/&#34;&gt;&lt;code&gt;locator.boundingBox()&lt;/code&gt;&lt;/a&gt;, which returns the bounding box of an element as a rectangle with position and size information. This method provides essential geometric data about elements on the page, making it valuable for visual testing, and layout verification.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;locator.boundingBox()&lt;/code&gt; is recommended over &lt;code&gt;elementHandle.boundingBox()&lt;/code&gt; because locators have built-in auto-waiting and retry logic, making them more resilient to dynamic content and DOM changes. While element handles can become stale if the page updates, locators represent a live query that gets re-evaluated, ensuring more reliable test execution.&lt;/p&gt;
&lt;p&gt;The method returns a rectangle object with &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;height&lt;/code&gt; properties, or &lt;code&gt;null&lt;/code&gt; if the element is not visible:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Get bounding box of an element
const submitButton = page.locator(&amp;#39;button[type=&amp;#34;submit&amp;#34;]&amp;#39;);
const rect = await submitButton.boundingBox();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;locator-filtering-5114-5150&#34;&gt;Locator filtering &lt;a href=&#34;https://github.com/grafana/k6/pull/5114&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5114&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5150&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5150&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports filtering options for locators, allowing you to create more precise and reliable element selections. This enhancement improves the robustness of your tests by enabling you to target elements that contain or exclude specific text, reducing reliance on brittle CSS selectors.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/filter/&#34;&gt;&lt;strong&gt;&lt;code&gt;locator.filter()&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; creates a new &lt;code&gt;locator&lt;/code&gt; that matches only elements containing or excluding specified text.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Filter list items that contain specific text
const product2Item = page
  .locator(&amp;#39;li&amp;#39;)
  .filter({ hasText: &amp;#39;Product 2&amp;#39; });

// Filter items that do NOT contain specific text using regex
const otherProducts = page
  .locator(&amp;#39;li&amp;#39;)
  .filter({ hasNotText: /Product 2/ });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s also possible to filter locators during their creation with options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;page.locator(selector, options)&lt;/code&gt;&lt;/strong&gt; creates page locators with optional text filtering:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Create locators with text filtering during creation
const submitButton = page.locator(&amp;#39;button&amp;#39;, { hasText: &amp;#39;Submit Order&amp;#39; });
await submitButton.click();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;frame.locator(selector, options)&lt;/code&gt;&lt;/strong&gt; creates frame locators with optional text filtering:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Filter elements within frame context
const frame = page.mainFrame();
const input = frame.locator(&amp;#39;input&amp;#39;, { hasNotText: &amp;#39;Disabled&amp;#39; });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;locator.locator(selector, options)&lt;/code&gt;&lt;/strong&gt; chains locators with optional text filtering:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Chain locators with filtering options
await page
  .locator(&amp;#39;[data-testid=&amp;#34;inventory&amp;#34;]&amp;#39;)
  .locator(&amp;#39;[data-item=&amp;#34;apples&amp;#34;]&amp;#39;, { hasText: &amp;#39;Green&amp;#39; })
  .click();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;frameLocator.locator(selector, options)&lt;/code&gt;&lt;/strong&gt; create locators within iframe content with optional text filtering:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Filter elements within iframe content
const iframe = page.locator(&amp;#39;iframe&amp;#39;).contentFrame();
await iframe.locator(&amp;#39;button&amp;#39;, { hasText: &amp;#39;Submit Payment&amp;#39; }).click();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;framegetby-locatorgetby-framelocatorgetby-5105-5106-5135&#34;&gt;&lt;code&gt;frame.getBy*&lt;/code&gt;, &lt;code&gt;locator.getBy*&lt;/code&gt;, &lt;code&gt;frameLocator.getBy*&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5105&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5105&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5106&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5106&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5135&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5135&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports all &lt;code&gt;getBy*&lt;/code&gt; methods on &lt;code&gt;frame&lt;/code&gt;, &lt;code&gt;locator&lt;/code&gt;, and &lt;code&gt;frameLocator&lt;/code&gt; types, expanding on the &lt;code&gt;page.getBy*&lt;/code&gt; APIs introduced in v1.2.1. This enhancement provides consistent element targeting across all browser automation contexts, improving Playwright compatibility and offering more flexible testing workflows. The available methods on all types are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getByRole()&lt;/code&gt; - Find elements by ARIA role&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getByText()&lt;/code&gt; - Find elements by text content&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getByLabel()&lt;/code&gt; - Find elements by associated label text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getByPlaceholder()&lt;/code&gt; - Find elements by placeholder text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getByAltText()&lt;/code&gt; - Find elements by alt text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getByTitle()&lt;/code&gt; - Find elements by title attribute&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getByTestId()&lt;/code&gt; - Find elements by data-testid attribute&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;examples-across-different-types&#34;&gt;Examples across different types&lt;/h4&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Frame context
const frame = page.mainFrame();
await frame.getByRole(&amp;#39;button&amp;#39;, { name: &amp;#39;Submit&amp;#39; }).click();
await frame.getByLabel(&amp;#39;Email&amp;#39;).fill(&amp;#39;user@example.com&amp;#39;);

// Locator context (for scoped searches)
const form = page.locator(&amp;#39;form.checkout&amp;#39;);
await form.getByRole(&amp;#39;textbox&amp;#39;, { name: &amp;#39;Card number&amp;#39; }).fill(&amp;#39;4111111111111111&amp;#39;);
await form.getByTestId(&amp;#39;submit-button&amp;#39;).click();

// FrameLocator context (for iframe content)
const paymentFrame = page.locator(&amp;#39;iframe&amp;#39;).contentFrame();
await paymentFrame.getByLabel(&amp;#39;Cardholder name&amp;#39;).fill(&amp;#39;John Doe&amp;#39;);
await paymentFrame.getByRole(&amp;#39;button&amp;#39;, { name: &amp;#39;Pay now&amp;#39; }).click();

// Chaining for precise targeting
await page
  .locator(&amp;#39;.product-list&amp;#39;)
  .getByText(&amp;#39;Premium Plan&amp;#39;)
  .getByRole(&amp;#39;button&amp;#39;, { name: &amp;#39;Select&amp;#39; })
  .click();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This expansion makes k6 browser automation more versatile and aligns with modern testing practices where element targeting by semantic attributes (roles, labels, text) is preferred over fragile CSS and XPath selectors.&lt;/p&gt;
&lt;h3 id=&#34;pagewaitforresponse-5002&#34;&gt;&lt;code&gt;page.waitForResponse&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5002&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5002&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/waitforresponse/&#34;&gt;&lt;code&gt;page.waitForResponse()&lt;/code&gt;&lt;/a&gt;, which allows you to wait for HTTP responses that match specific URL patterns during browser automation. This method is particularly valuable for testing scenarios where you need to ensure specific network requests complete before proceeding with test actions.&lt;/p&gt;
&lt;p&gt;The method supports multiple URL pattern matching strategies:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Wait for exact URL match
await page.waitForResponse(&amp;#39;https://api.example.com/data&amp;#39;);

// Wait for regex pattern match
await page.waitForResponse(/\/api\/.*\.json$/);

// Use with Promise.all for coordinated actions
await Promise.all([
  page.waitForResponse(&amp;#39;https://api.example.com/user-data&amp;#39;),
  page.click(&amp;#39;button[data-testid=&amp;#34;load-user-data&amp;#34;]&amp;#39;)
]);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This complements the existing &lt;code&gt;waitForURL&lt;/code&gt; method by focusing on HTTP responses rather than navigation events, providing more granular control over network-dependent test scenarios.&lt;/p&gt;
&lt;p&gt;Thank you, @HasithDeAlwis, for contributing this feature.&lt;/p&gt;
&lt;h2 id=&#34;roadmap&#34;&gt;Roadmap&lt;/h2&gt;
&lt;h3 id=&#34;deprecation-of-first-input-delay-fid-web-vital&#34;&gt;Deprecation of First Input Delay (FID) Web Vital&lt;/h3&gt;
&lt;p&gt;Following the official &lt;a href=&#34;https://web.dev/blog/fid&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;web vitals guidance&lt;/a&gt;, First Input Delay (FID) is no longer a Core Web Vital as of September 9, 2024, having been replaced by Interaction to Next Paint (INP). The k6 browser module already emits INP metrics, and we&amp;rsquo;re planning to deprecate FID support to align with industry standards.&lt;/p&gt;
&lt;p&gt;FID only measures the delay before the browser runs your event handler, so it ignores the time your code takes and the delay to paint the UI—often underestimating how slow an interaction feels. INP captures the full interaction latency (input delay &#43; processing &#43; next paint) across a page’s interactions, so it better reflects real user-perceived responsiveness and is replacing FID.&lt;/p&gt;
&lt;h4 id=&#34;planned-timeline&#34;&gt;Planned timeline&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;v1.4.x&#43;: Deprecation warnings will appear in the terminal when FID metrics are used &lt;a href=&#34;https://github.com/grafana/k6/issues/5179&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5179&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Grafana Cloud k6: Similar deprecation warnings will be shown in the cloud platform.&lt;/li&gt;
&lt;li&gt;v2.0: Complete removal of FID metric support.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;action-required&#34;&gt;Action required&lt;/h4&gt;
&lt;p&gt;If you&amp;rsquo;re currently using FID in your test scripts for thresholds or relying on it in external integrations, you should migrate to using INP as soon as possible.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Instead of relying on FID
export const options = {
  thresholds: {
    // &amp;#39;browser_web_vital_fid&amp;#39;: [&amp;#39;p(95)&amp;lt;100&amp;#39;], // Deprecated
    &amp;#39;browser_web_vital_inp&amp;#39;: [&amp;#39;p(95)&amp;lt;200&amp;#39;], // Use INP instead
  },
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This change ensures k6 browser testing stays aligned with modern web performance best practices and Core Web Vitals standards.&lt;/p&gt;
&lt;h3 id=&#34;opentelemetry-stabilization&#34;&gt;OpenTelemetry stabilization&lt;/h3&gt;
&lt;p&gt;We aim to stabilize OpenTelemetry&amp;rsquo;s experimental metric output, promoting vendor neutrality for metric outputs. OpenTelemetry is becoming the standard protocol for metric format in observability. Our goal is to enable k6 users to utilize their preferred metric backend storage without any technological imposition.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to the &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v1.3.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-130-release-notes">Version 1.3.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 v1.3.0 is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Browser module gets:
&lt;ul>
&lt;li>&lt;code>locator.locator&lt;/code>, &lt;code>locator.contentFrame&lt;/code>, and &lt;code>FrameLocator.locator&lt;/code> for powerful locator chaining and iframe handling.&lt;/li>
&lt;li>&lt;code>locator|frame|FrameLocator.getBy*&lt;/code> for targeting elements without relying on brittle CSS selectors.&lt;/li>
&lt;li>&lt;code>locator.filter&lt;/code> for filtering locators for more precise element targeting.&lt;/li>
&lt;li>&lt;code>locator.boundingBox&lt;/code> for retrieving element geometry.&lt;/li>
&lt;li>&lt;code>page.waitForResponse&lt;/code> for waiting on specific HTTP responses.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="deprecations">Deprecations&lt;/h2>
&lt;h3 id="a-new-summary-mode-disabled-has-been-introduced-to-replace-the-no-summary-option-5118">A new summary mode &lt;code>disabled&lt;/code> has been introduced to replace the &amp;ldquo;no summary&amp;rdquo; option &lt;a href="https://github.com/grafana/k6/pull/5118" target="_blank" rel="noopener noreferrer">#5118&lt;/a>&lt;/h3>
&lt;p>The &lt;code>--no-summary&lt;/code> flag and its corresponding environment variable &lt;code>K6_NO_SUMMARY&lt;/code> have been deprecated in favor of
the new &lt;code>disabled&lt;/code> summary mode. This change unifies the configuration experience for controlling the end-of-test summary.&lt;/p></description></item><item><title>Version 1.2.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.2.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.2.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-120-release-notes&#34;&gt;Version 1.2.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 v1.2.0 is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic extension resolution (previously Binary Provisioning) enabled for everyone&lt;/li&gt;
&lt;li&gt;gRPC gets better handling of &lt;code&gt;NaN&lt;/code&gt; and &lt;code&gt;Infinity&lt;/code&gt; float values and easier health check&lt;/li&gt;
&lt;li&gt;Browser module gets &lt;code&gt;page.route&lt;/code&gt;, all the &lt;code&gt;page.getBy*&lt;/code&gt; APIs, &lt;code&gt;locator.all()&lt;/code&gt;, and &lt;code&gt;page.waitForURL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;As per our &lt;a href=&#34;/docs/k6/latest/reference/versioning-and-stability-guarantees/&#34;&gt;stability guarantees&lt;/a&gt;,
breaking changes across minor releases are allowed only for experimental features.&lt;/p&gt;
&lt;h3 id=&#34;breaking-changes-for-experimental-modules&#34;&gt;Breaking changes for experimental modules&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The experimental Open Telemetry and Prometheus outputs now default to TLSv1.3. This should&amp;rsquo;ve been the default to begin with. It is not expected that anyone should be affected, apart from making it more secure for the metrics output to send messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;automatic-extension-resolution&#34;&gt;Automatic extension resolution&lt;/h3&gt;
&lt;p&gt;k6 extensions allow you to add custom functionality to your tests, such as connecting to databases, message queues, or specialized networking protocols. Previously, using extensions &lt;a href=&#34;/docs/k6/latest/extensions/run/build-k6-binary-using-go/&#34;&gt;required manual building&lt;/a&gt; of a custom k6 binary with the extensions compiled in. This new version introduces the &lt;em&gt;Automatic Extension Resolution&lt;/em&gt; functionality, previously named Binary Provisioning, which is enabled by default and automatically detects when your script imports extensions and handles the complexity of provisioning the right k6 binary for you.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import faker from &amp;#34;k6/x/faker&amp;#34;;

export default function () {
  console.log(faker.person.firstName());
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The previous experimental versions only supported official extensions. &lt;a href=&#34;https://github.com/grafana/k6/pull/4922&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4922&lt;/a&gt; added the support to use any extension listed in the &lt;a href=&#34;/docs/k6/latest/extensions/explore/&#34;&gt;community list&lt;/a&gt; by setting the &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;K6_ENABLE_COMMUNITY_EXTENSIONS=true k6 run script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Note, Community extensions are only supported for local test executions (using &lt;code&gt;k6 run&lt;/code&gt; or &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;). When running tests on Grafana Cloud k6, only official extensions are allowed.&lt;/p&gt;
&lt;p&gt;Check out the new &lt;a href=&#34;/docs/k6/latest/extensions/&#34;&gt;extensions documentation&lt;/a&gt; for additional details.&lt;/p&gt;
&lt;h3 id=&#34;handling-of-nan-and-infinity-float-values-in-grpc-4631&#34;&gt;Handling of NaN and Infinity float values in gRPC &lt;a href=&#34;https://github.com/grafana/k6/pull/4631&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4631&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Previously, float values of &lt;code&gt;NaN&lt;/code&gt; or &lt;code&gt;Infinity&lt;/code&gt; were marshalled as &lt;code&gt;null&lt;/code&gt;. This has now changed to use their string representation, aligning with other gRPC APIs.&lt;/p&gt;
&lt;p&gt;There are no changes required in the scripts.&lt;/p&gt;
&lt;p&gt;This is also the first contribution by @ariasmn. Thank you @ariasmn for taking the time to make the PR and answer all our questions.&lt;/p&gt;
&lt;h3 id=&#34;health-check-for-grpc-apis-4853&#34;&gt;Health check for gRPC APIs &lt;a href=&#34;https://github.com/grafana/k6/pull/4853&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4853&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The k6 gRPC module now has a &lt;code&gt;client.healthCheck()&lt;/code&gt; method that simplifies checking the status of a gRPC service. This method eliminates the need for manual &lt;code&gt;invoke&lt;/code&gt; calls, making it particularly useful for readiness checks and service discovery.&lt;/p&gt;
&lt;p&gt;Before, you had to write boilerplate code to perform a health check:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import grpc from &amp;#39;k6/grpc&amp;#39;;

const client = new grpc.Client();
// ...
const response = client.invoke(&amp;#39;grpc.health.v1.Health/Check&amp;#39;, { service: &amp;#39;my-service&amp;#39; });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this with the &lt;code&gt;healthCheck()&lt;/code&gt; method:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import grpc from &amp;#39;k6/grpc&amp;#39;;

const client = new grpc.Client();
client.connect(&amp;#39;grpc.test.k6.io:443&amp;#39;);

// Check the health of a specific service
const response = client.healthCheck(&amp;#39;my-service&amp;#39;);

// Check the health of the overall gRPC server
const overallResponse = client.healthCheck();

client.close();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Check out the &lt;a href=&#34;/docs/k6/latest/using-k6/protocols/grpc/#health-checking-protocol&#34;&gt;client.healthCheck&lt;/a&gt; documentation for additional details.
Thank you, @tbourrely, for contributing this feature.&lt;/p&gt;
&lt;h3 id=&#34;assertions-library-preview-4067&#34;&gt;Assertions Library (Preview) &lt;a href=&#34;https://github.com/grafana/k6/issues/4067&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4067&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 now provides an &lt;a href=&#34;/docs/k6/latest/using-k6/assertions/&#34;&gt;assertions&lt;/a&gt; library to help you verify your application behaves as expected during testing.&lt;/p&gt;
&lt;p&gt;The library introduces the &lt;a href=&#34;/docs/k6/latest/javascript-api/jslib/testing/expect/&#34;&gt;&lt;code&gt;expect&lt;/code&gt;&lt;/a&gt; function with a set of expressive matchers. Pass a value to &lt;a href=&#34;/docs/k6/latest/javascript-api/jslib/testing/expect/&#34;&gt;&lt;code&gt;expect()&lt;/code&gt;&lt;/a&gt; and chain it with a matcher that defines the expected outcome. The library caters to both protocol testing &lt;a href=&#34;/docs/k6/latest/using-k6/protocols/&#34;&gt;HTTP/API&lt;/a&gt; and &lt;a href=&#34;/docs/k6/latest/using-k6-browser/&#34;&gt;browser&lt;/a&gt; testing scenarios.&lt;/p&gt;
&lt;p&gt;The API is inspired by Playwright&amp;rsquo;s assertion syntax, offering a fluent interface for more readable and reliable tests.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { expect } from &amp;#39;https://jslib.k6.io/k6-testing/0.5.0/index.js&amp;#39;;
import { browser } from &amp;#39;k6/browser&amp;#39;;
import http from &amp;#39;k6/http&amp;#39;;

export function protocolTest() {
  // Get the home page of k6&amp;#39;s Quick Pizza app
  const response = http.get(&amp;#39;https://quickpizza.grafana.com/&amp;#39;);

  // Simple assertions
  expect(response.status).toBe(200);
  expect(response.error).toEqual(&amp;#39;&amp;#39;);
  expect(response.body).toBeDefined();
}

export async function browserTest() {
  const page = await browser.newPage();

  try {
    await page.goto(&amp;#39;https://quickpizza.grafana.com/&amp;#39;);

    // Assert the &amp;#34;Pizza Please&amp;#34; button is visible
    await expect(page.locator(&amp;#39;button[name=pizza-please]&amp;#39;)).toBeVisible();
  } finally {
    await page.close();
  }
}

export const options = {
  scenarios: {
    // Protocol tests
    protocol: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      vus: 1,
      iterations: 1,
      exec: &amp;#39;protocolTest&amp;#39;,
    },

    // Browser tests
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
      exec: &amp;#39;browserTest&amp;#39;,
    },
  },
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 id=&#34;preview-feature&#34;&gt;Preview feature&lt;/h4&gt;
&lt;p&gt;This feature is ready to use, but still in preview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No breaking changes are neither planned, nor expected.&lt;/li&gt;
&lt;li&gt;Some functionality may be missing or rough around the edges.&lt;/li&gt;
&lt;li&gt;We expect to keep adding matchers and improving coverage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We welcome your feedback, and invite you to share your suggestions and contributions on &lt;a href=&#34;https://github.com/grafana/k6-jslib-testing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;add-pagegetbyrole-api-4843&#34;&gt;Add &lt;code&gt;page.getByRole&lt;/code&gt; API &lt;a href=&#34;https://github.com/grafana/k6/pull/4843&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4843&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;code&gt;page.getByRole()&lt;/code&gt;, which allows you to locate elements based on their &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ARIA roles&lt;/a&gt;. This provides a more semantic and accessible way to find elements, making your tests more robust and aligned with how users actually interact with web applications.&lt;/p&gt;
&lt;p&gt;ARIA roles represent the purpose or function of an element (like button, link, textbox, etc.), making them excellent selectors for testing since they&amp;rsquo;re less likely to change when the UI is refactored compared to CSS classes or IDs.&lt;/p&gt;
&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Find elements by role
await page.getByRole(&amp;#39;button&amp;#39;).click();

// Find elements by role and accessible name
await page.getByRole(&amp;#39;button&amp;#39;, { name: &amp;#39;Submit&amp;#39; }).click();

// `name` works with regex too
await page.getByRole(&amp;#39;textbox&amp;#39;, { name: /^Username$/ }).fill(&amp;#39;admin&amp;#39;);

// Work with specific states
await page.getByRole(&amp;#39;checkbox&amp;#39;, { name: &amp;#39;Accept terms&amp;#39;, checked: true }).click();

// Find headings by level
await page.getByRole(&amp;#39;heading&amp;#39;, { level: 2, name: &amp;#39;Section Title&amp;#39; }).textContent();

### Add `page.getByAltText` [#4881](https://github.com/grafana/k6/pull/4881)

The browser module now includes `page.getByAltText()`, which provides a convenient way to select elements that have an `alt` text attribute. This is particularly useful for locating images or other elements that rely on alternative text for accessibility.

Previously, you would have to use CSS or XPath selectors to find these elements:
```javascript
// Using CSS selector
const locator = page.locator(&amp;#39;img[alt=&amp;#34;World Map&amp;#34;]&amp;#39;);

// Using XPath selector
const locator = page.locator(&amp;#39;//img[@alt=&amp;#34;World Map&amp;#34;]&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this by using &lt;code&gt;getByAltText()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const locator = page.getByAltText(&amp;#39;World Map&amp;#39;);

// Find an image with alt text that starts with &amp;#39;World&amp;#39;
const locator = page.getByAltText(/^World/);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-pagegetbylabel-4890&#34;&gt;Add &lt;code&gt;page.getByLabel&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4890&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4890&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now includes &lt;code&gt;page.getByLabel()&lt;/code&gt;, which provides a convenient way to locate form elements and other interactive components by their associated label text. This method works with both explicit &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements and elements that have an &lt;code&gt;aria-label&lt;/code&gt; attribute, making it particularly useful for finding form inputs, buttons, and other interactive elements.&lt;/p&gt;
&lt;p&gt;Previously, you would need to use XPath selectors to find elements by their label text, since CSS selectors cannot easily handle the relationship between labels and form elements:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Using XPath to find input by label text  
const locator = page.locator(&amp;#39;//label[text()=&amp;#34;Password&amp;#34;]&amp;#39;);

// Or using aria-label with CSS
const locator = page.locator(&amp;#39;[aria-label=&amp;#34;Username&amp;#34;]&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this with &lt;code&gt;getByLabel()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Works with both &amp;lt;label&amp;gt; elements and aria-label attributes
const passwordInput = page.getByLabel(&amp;#39;Password&amp;#39;);

// Works with regex too
const usernameInput = page.getByLabel(/^Username$/);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-pagegetbyplaceholder-4904&#34;&gt;Add &lt;code&gt;page.getByPlaceholder&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4904&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4904&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now includes &lt;code&gt;page.getByPlaceholder()&lt;/code&gt;, which provides a convenient way to locate form elements by their placeholder text. This is particularly useful for finding input fields, textareas, and other form controls that use placeholder text to guide user input.&lt;/p&gt;
&lt;p&gt;Previously, you would need to use CSS or XPath selectors to find elements by their placeholder attribute:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Using CSS selector
const locator = page.locator(&amp;#39;input[placeholder=&amp;#34;Enter your name&amp;#34;]&amp;#39;);

// Using XPath selector  
const locator = page.locator(&amp;#39;//input[@placeholder=&amp;#34;Enter your name&amp;#34;]&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this with &lt;code&gt;getByPlaceholder()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const nameInput = page.getByPlaceholder(&amp;#39;Enter your name&amp;#39;);

// Works with regex too
const emailInput = page.getByPlaceholder(/^Email/);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-pagegetbytitle-4910&#34;&gt;Add &lt;code&gt;page.getByTitle&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4910&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4910&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now includes &lt;code&gt;page.getByTitle()&lt;/code&gt;, which provides a convenient way to locate elements by their &lt;code&gt;title&lt;/code&gt; attribute. This is particularly useful for finding tooltips, buttons, or any other elements that use the &lt;code&gt;title&lt;/code&gt; attribute to provide extra information.&lt;/p&gt;
&lt;p&gt;Previously, you would need to use CSS or XPath selectors to find these elements:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Using CSS selector
const locator = page.locator(&amp;#39;div[title=&amp;#34;Information box&amp;#34;]&amp;#39;);

// Using XPath selector
const locator = page.locator(&amp;#39;//div[@title=&amp;#34;Information box&amp;#34;]&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this with &lt;code&gt;getByTitle()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const infoBox = page.getByTitle(&amp;#39;Information box&amp;#39;);

// Works with regex too
const saveButton = page.getByTitle(/^Save/);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-pagegetbytestid-4911&#34;&gt;Add &lt;code&gt;page.getByTestId&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4911&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4911&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now includes &lt;code&gt;page.getByTestId()&lt;/code&gt;, which provides a convenient way to locate elements by their &lt;code&gt;data-testid&lt;/code&gt; attribute. This is particularly useful for creating resilient tests that are not affected by changes to the UI, since &lt;code&gt;data-testid&lt;/code&gt; attributes are specifically added for testing purposes and are not expected to change.&lt;/p&gt;
&lt;p&gt;Previously, you would need to use CSS or XPath selectors to find these elements:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Using CSS selector
const locator = page.locator(&amp;#39;button[data-testid=&amp;#34;submit-button&amp;#34;]&amp;#39;);

// Using XPath selector
const locator = page.locator(&amp;#39;//button[@data-testid=&amp;#34;submit-button&amp;#34;]&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this with &lt;code&gt;getByTestId()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const submitButton = page.getByTestId(&amp;#39;submit-button&amp;#39;);

// Works with regex too
const usernameInput = page.getByTestId(/^username/);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-pagegetbytext-4912&#34;&gt;Add &lt;code&gt;page.getByText&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4912&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4912&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now includes &lt;code&gt;page.getByText()&lt;/code&gt;, which allows you to locate elements by their text content. This provides a convenient way to find elements like buttons, links, and other interactive components that are identified by their visible text.&lt;/p&gt;
&lt;p&gt;Previously, you would need to use XPath selectors to find elements by their text content, since CSS selectors cannot directly query the text of an element:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Using XPath selector
const locator = page.locator(&amp;#39;//div[text()=&amp;#34;Hello World&amp;#34;]&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, you can simplify this with &lt;code&gt;getByText()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const helloWorldElement = page.getByText(&amp;#39;Hello World&amp;#39;);

// Works with regex too
const submitButton = page.getByText(/^Submit/);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-pageroute-4953-4961-4971-4985&#34;&gt;Add &lt;code&gt;page.route&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4953&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4953&lt;/a&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4961&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4961&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/4971&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4971&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/4985&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4985&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports &lt;code&gt;page.route()&lt;/code&gt;, which allows you to intercept and handle network requests before they are sent. This is particularly useful for testing scenarios where you need to mock API responses, block certain resources, or modify request behavior.&lt;/p&gt;
&lt;p&gt;The route handler receives a &lt;code&gt;route&lt;/code&gt; object that provides methods to &lt;code&gt;abort()&lt;/code&gt;, &lt;code&gt;continue()&lt;/code&gt;, or &lt;code&gt;fulfill()&lt;/code&gt; the request.&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;page.route()&lt;/code&gt; to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Block requests&lt;/strong&gt;: Prevent certain resources from loading (e.g., images, ads, analytics) with &lt;code&gt;abort()&lt;/code&gt;.

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Block all image requests
await page.route(/(\.png$)|(\.jpg$)|(\.jpeg$)/, async (route) =&amp;gt; {
    await route.abort();
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mock responses&lt;/strong&gt;: Return custom responses without hitting real endpoints with &lt;code&gt;fulfill()&lt;/code&gt;.

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Mock API responses
await page.route(&amp;#39;**/api/users&amp;#39;, async (route) =&amp;gt; {
    await route.fulfill({
        status: 200,
        contentType: &amp;#39;application/json&amp;#39;,
        body: JSON.stringify([{ id: 1, name: &amp;#39;Mock User&amp;#39; }])
    });
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modify requests&lt;/strong&gt;: Change headers, URL, or request body before they&amp;rsquo;re sent with &lt;code&gt;continue()&lt;/code&gt;.

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Continue with modified headers
await page.route(&amp;#39;**/api/**&amp;#39;, async (route) =&amp;gt; {
    await route.continue({
        headers: {
        ...route.request().headers(),
        &amp;#39;Authorization&amp;#39;: &amp;#39;Bearer mock-token&amp;#39;
        }
    });
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;add-locatorall-4899&#34;&gt;Add &lt;code&gt;locator.all()&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4899&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4899&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports the &lt;code&gt;locator.all()&lt;/code&gt; method, which returns an array of locators for all elements matching the selector. This is particularly useful when you need to interact with multiple similar elements on a page, such as items in a list or multiple buttons with the same styling.&lt;/p&gt;
&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Get all list items and iterate through them
const items = await page.locator(&amp;#39;li&amp;#39;).all();
for (const item of items) {
  console.log(await item.textContent());
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-waitforurl-in-frame-and-page-4917-4920&#34;&gt;Add &lt;code&gt;waitForURL&lt;/code&gt; in &lt;code&gt;frame&lt;/code&gt; and &lt;code&gt;page&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4917&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4917&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/4920&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4920&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now includes the &lt;code&gt;waitForURL&lt;/code&gt; method for both &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;frame&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;As a prerequiste to this enhancement, &lt;code&gt;waitForNavigation&lt;/code&gt; now accepts a &lt;code&gt;url&lt;/code&gt; option. This also allows you to wait for a specific URL during navigation. &lt;strong&gt;It is advised that you work with &lt;code&gt;waitForURL&lt;/code&gt; instead&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;waitForURL&lt;/code&gt; method first checks if the current page URL already matches the expected pattern. If it does, it waits for the load state to complete. Otherwise, it waits for a navigation to the specified URL. This approach prevents race conditions where a page might complete navigation before the wait condition is set up, which is particularly useful when dealing with pages that perform multiple redirects. It supports both string patterns and regular expressions:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Wait for navigation to a specific URL
await Promise.all([
  page.waitForURL(&amp;#39;https://quickpizza.grafana.com/my_messages.php&amp;#39;),
  page.locator(&amp;#39;a[href=&amp;#34;/my_messages.php&amp;#34;]&amp;#39;).click(),
]);

// Using regex pattern
await Promise.all([
  page.waitForURL(/.*\/contacts\.php.*/),
  page.locator(&amp;#39;a[href^=&amp;#34;/contacts.php&amp;#34;]&amp;#39;).click()
]);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;While &lt;code&gt;waitForURL&lt;/code&gt; provides a convenient way to wait for specific URLs, we still recommend using element-based waiting strategies or the locator API with its built-in auto-waiting capabilities for more reliable tests.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v1.2.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-120-release-notes">Version 1.2.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 v1.2.0 is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Automatic extension resolution (previously Binary Provisioning) enabled for everyone&lt;/li>
&lt;li>gRPC gets better handling of &lt;code>NaN&lt;/code> and &lt;code>Infinity&lt;/code> float values and easier health check&lt;/li>
&lt;li>Browser module gets &lt;code>page.route&lt;/code>, all the &lt;code>page.getBy*&lt;/code> APIs, &lt;code>locator.all()&lt;/code>, and &lt;code>page.waitForURL&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>As per our &lt;a href="/docs/k6/latest/reference/versioning-and-stability-guarantees/">stability guarantees&lt;/a>,
breaking changes across minor releases are allowed only for experimental features.&lt;/p></description></item><item><title>Version 1.1.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.1.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.1.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-110-release-notes&#34;&gt;Version 1.1.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v1.1.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;nth&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, and &lt;code&gt;last&lt;/code&gt; methods for the browser module&amp;rsquo;s Locator API &lt;a href=&#34;https://github.com/grafana/k6/pull/4797&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4797&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/4825&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4825&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt; module has been removed as its functionality is available globally.&lt;/li&gt;
&lt;li&gt;Group results in the &lt;code&gt;full&lt;/code&gt; end-of-test summary are now sorted as in code and properly indented.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;As per our &lt;a href=&#34;/docs/k6/latest/reference/versioning-and-stability-guarantees/&#34;&gt;stability guarantees&lt;/a&gt;,
breaking changes across minor releases are allowed only for experimental features.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;breaking-changes-for-experimental-modules&#34;&gt;Breaking changes for experimental modules&lt;/h3&gt;
&lt;h4 id=&#34;remove-experimental-k6experimentalwebcrypto-module-4851&#34;&gt;Remove experimental &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/k6/pull/4851&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4851&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;/docs/k6/latest/javascript-api/crypto/&#34;&gt;WebCrypto API&lt;/a&gt; has been available globally since &lt;code&gt;v1.0.0-rc1&lt;/code&gt; (or &lt;code&gt;v0.58.0&lt;/code&gt;), and now the experimental import (&lt;code&gt;k6/experimental/webcrypto&lt;/code&gt;) is no longer available.&lt;/p&gt;
&lt;p&gt;The required change for users is to remove the &lt;code&gt;import&lt;/code&gt;; the rest of the code should work.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;new-count-method-for-the-browser-modules-locator-api-4797&#34;&gt;New &lt;code&gt;count&lt;/code&gt; method for the browser module&amp;rsquo;s Locator API &lt;a href=&#34;https://github.com/grafana/k6/pull/4797&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4797&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The new &lt;code&gt;locator.Count&lt;/code&gt; method returns the number of elements matching the &lt;code&gt;locator&lt;/code&gt;. Unlike other Locator API methods, &lt;code&gt;locator.Count&lt;/code&gt; returns the result immediately and doesn&amp;rsquo;t wait for the elements to be visible.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { expect } from &amp;#34;https://jslib.k6.io/k6-testing/0.4.0/index.js&amp;#34;;
import { browser } from &amp;#39;k6/browser&amp;#39;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
}

export default async function () {
  const page = await browser.newPage()
  await page.goto(&amp;#39;https://quickpizza.grafana.com/login&amp;#39;)

  expect(await page.locator(&amp;#39;input&amp;#39;).count()).toEqual(3);

  await page.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;new-nth-first-and-last-methods-for-the-browser-modules-locator-api-4825&#34;&gt;New &lt;code&gt;nth&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt; and &lt;code&gt;last&lt;/code&gt; methods for the browser module&amp;rsquo;s Locator API &lt;a href=&#34;https://github.com/grafana/k6/pull/4825&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4825&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The new Locator API methods, &lt;code&gt;nth&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, and &lt;code&gt;last&lt;/code&gt;, can select a single element from multiple elements matched by a &lt;code&gt;locator&lt;/code&gt;. For example, selecting a single item from a catalogue of items on an e-commerce website. Because items in this catalogue generally change often and selecting an exact element may fail in future test runs, the new methods help to prevent flaky tests, leading to more reliable tests.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { expect } from &amp;#34;https://jslib.k6.io/k6-testing/0.4.0/index.js&amp;#34;;
import { browser } from &amp;#39;k6/browser&amp;#39;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
}

export default async function () {
  const page = await browser.newPage()
  await page.goto(&amp;#39;https://quickpizza.grafana.com&amp;#39;)

  await expect(await page.locator(&amp;#39;p&amp;#39;).first()).toContainText(&amp;#39;QuickPizza&amp;#39;);
  await expect(await page.locator(&amp;#39;p&amp;#39;).nth(4)).toContainText(&amp;#39;QuickPizza Labs.&amp;#39;);
  await expect(await page.locator(&amp;#39;p&amp;#39;).last()).toContainText(&amp;#39;Contribute to QuickPizza&amp;#39;);

  await page.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v1.1.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-110-release-notes">Version 1.1.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v1.1.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>New &lt;code>count&lt;/code>, &lt;code>nth&lt;/code>, &lt;code>first&lt;/code>, and &lt;code>last&lt;/code> methods for the browser module&amp;rsquo;s Locator API &lt;a href="https://github.com/grafana/k6/pull/4797" target="_blank" rel="noopener noreferrer">#4797&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/4825" target="_blank" rel="noopener noreferrer">#4825&lt;/a>&lt;/li>
&lt;li>The &lt;code>k6/experimental/webcrypto&lt;/code> module has been removed as its functionality is available globally.&lt;/li>
&lt;li>Group results in the &lt;code>full&lt;/code> end-of-test summary are now sorted as in code and properly indented.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>&lt;em>As per our &lt;a href="/docs/k6/latest/reference/versioning-and-stability-guarantees/">stability guarantees&lt;/a>,
breaking changes across minor releases are allowed only for experimental features.&lt;/em>&lt;/p></description></item><item><title>Version 1.0.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.0.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.0.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-100-release-notes&#34;&gt;Version 1.0.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;Grafana k6 v1.0 is here! 🎉&lt;/p&gt;
&lt;p&gt;After 9 years of iteration and countless community contributions, we&amp;rsquo;re thrilled to announce &lt;strong&gt;Grafana k6 v1.0&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;While many features and capabilities in this release were introduced gradually in previous versions, &lt;strong&gt;k6 v1.0 marks a turning point&lt;/strong&gt;: a commitment to stability, formal support guarantees, and transparency in how we evolve and develop the project from here. This milestone is more than a version number; it&amp;rsquo;s about trust, reliability, and empowering you to test confidently.&lt;/p&gt;
&lt;h2 id=&#34;thank-you-k6-community-&#34;&gt;Thank you, k6 community! 🌍&lt;/h2&gt;
&lt;p&gt;This release wouldn&amp;rsquo;t be possible without you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐️ 27000&#43; GitHub stars.&lt;/li&gt;
&lt;li&gt;🧠 9000&#43; git commits.&lt;/li&gt;
&lt;li&gt;🤝 200&#43; contributors.&lt;/li&gt;
&lt;li&gt;🔁 Countless test runs of any scale, in every timezone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s been amazing to watch k6 grow from a simple load testing command-line tool into a comprehensive reliability tool, used by teams worldwide and supported by a passionate and dedicated community.&lt;/p&gt;
&lt;p&gt;To everyone who filed bugs, built extensions and libraries, or championed k6:️
&lt;strong&gt;Thank you!&lt;/strong&gt; You&amp;rsquo;ve shaped what k6 is today. 🙇‍♂️&lt;/p&gt;
&lt;h2 id=&#34;whats-new-in-k6-10&#34;&gt;What&amp;rsquo;s New in k6 1.0?&lt;/h2&gt;
&lt;h3 id=&#34;1-stability-you-can-build-on&#34;&gt;1. Stability You Can Build On&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Semantic Versioning&lt;/strong&gt;: k6 now follows &lt;a href=&#34;https://semver.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Semantic Versioning 2.0&lt;/a&gt;. Breaking changes will only happen in major releases, with prior deprecation warnings.&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;2-Year Support Guarantees&lt;/strong&gt;: Every major version will be supported with critical fixes for &lt;strong&gt;at least two years&lt;/strong&gt;; upgrade on your schedule.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Public API Surface&lt;/strong&gt;: We&amp;rsquo;ve established a clearly delineated and supported API surface for the k6 codebase. Extensions, integrations, and projects building on top of the k6 code now have a stable foundation to rely on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🔎 Read more in our &lt;a href=&#34;/docs/k6/latest/reference/versioning-and-stability-guarantees/&#34;&gt;versioning and stability guarantees&lt;/a&gt; guide.&lt;/p&gt;
&lt;h3 id=&#34;2-first-class-typescript-support&#34;&gt;2. First-Class TypeScript Support&lt;/h3&gt;
&lt;p&gt;Write type-safe and maintainable tests—no transpilation needed. Simply save your file with a &lt;code&gt;.ts&lt;/code&gt; extension and run it directly using &lt;code&gt;k6 run script.ts&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;typescript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-typescript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;  

// PizzaRequest defines the request body format the quickpizza API expects
export interface PizzaRequest {
    maxCaloriesPerSlice: number;
    mustBeVegetarian: boolean;
}

export default function () {  
    const payload: PizzaRequest = {
        maxCaloriesPerSlice: 500, // Type-checked!  
        mustBeVegetarian: true,
    }

  http.post(
    &amp;#39;https://quickpizza.grafana.com/api/pizza&amp;#39;,
    JSON.stringify(payload),
    { 
        headers: { 
          &amp;#34;Content-Type&amp;#34;: &amp;#34;application/json&amp;#34;,
          &amp;#34;Authorization&amp;#34;: &amp;#34;Token &amp;#34; &amp;#43; &amp;#34;abcdef0123456789&amp;#34;
        } as Record&amp;lt;string, string&amp;gt;
    });  
}  &lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;3-extensions-made-simple&#34;&gt;3. Extensions Made Simple&lt;/h3&gt;
&lt;p&gt;With k6 v1.0, extensions now work out of the box in &lt;code&gt;k6 cloud run&lt;/code&gt; and &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;. Support for &lt;code&gt;k6 run&lt;/code&gt; is coming soon.&lt;/p&gt;
&lt;p&gt;✅ No more xk6 toolchain.
✅ No manual builds.
✅ Import an extension&amp;rsquo;s module and go.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import faker from &amp;#39;k6/x/faker&amp;#39;;

export default function () {
  console.log(faker.person.firstName());
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To try the experimental feature, first enable its feature flag, then run it on Grafana Cloud with the following command:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;K6_BINARY_PROVISIONING=true k6 cloud run script.js,&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;or if you want to run it locally and stream the results to Grafana Cloud then use:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;K6_BINARY_PROVISIONING=true k6 cloud run --local-execution script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;4-revamped-test-summary&#34;&gt;4. Revamped test summary&lt;/h3&gt;
&lt;p&gt;The new end-of-test summary makes it easier to &lt;strong&gt;understand results and spot issues&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📊 &lt;strong&gt;Hierarchical output&lt;/strong&gt;: Metrics are grouped by scenario, group, and category.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Improved thresholds &amp;amp; checks&lt;/strong&gt;: Clearer layout for faster debugging.&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;Multiple summary modes&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;compact&lt;/code&gt; (default): big picture results, focusing on essentials.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;full&lt;/code&gt;: full picture results, providing granularity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;k6 run --summary-mode=full script.ts&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;https://github.com/user-attachments/assets/02984ff9-6ed2-4eb2-9637-daf5058f2de6&#34;
  alt=&#34;end-of-test-summary&#34;/&gt;&lt;/p&gt;
&lt;h3 id=&#34;5-quality-of-life-upgrades&#34;&gt;5. Quality of Life Upgrades&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Stable modules: &lt;code&gt;k6/browser&lt;/code&gt;, &lt;code&gt;k6/net/grpc&lt;/code&gt;, and &lt;code&gt;k6/crypto&lt;/code&gt; are now production-ready.&lt;/li&gt;
&lt;li&gt;Improved Grafana Cloud integration: Stream local test results to Grafana Cloud with &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="version-100-release-notes">Version 1.0.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>Grafana k6 v1.0 is here! 🎉&lt;/p>
&lt;p>After 9 years of iteration and countless community contributions, we&amp;rsquo;re thrilled to announce &lt;strong>Grafana k6 v1.0&lt;/strong>.&lt;/p></description></item><item><title>Version 0.57.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.57.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.57.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0570-release-notes&#34;&gt;Version 0.57.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.57.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Introducing helpers for functional testing.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;k6 new&lt;/code&gt; command now supports templates and ProjectIDs.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;k6/experimental/csv&lt;/code&gt; module gets a new &lt;code&gt;asObjects&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;We no longer support the &lt;code&gt;k6/experimental/browser&lt;/code&gt; module, in favor of &lt;code&gt;k6/browser&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Moving most of non-public APIs to the &lt;code&gt;internal&lt;/code&gt; package.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/4161&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4161&lt;/a&gt; Drops &lt;code&gt;k6/experimental/browser&lt;/code&gt;. If you&amp;rsquo;re still using it, follow the 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6-browser/migrating-to-k6-v0-52/&#34;&gt;instructions&lt;/a&gt; to move to the graduated and stable &lt;code&gt;k6/browser&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/4133&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4133&lt;/a&gt; Moves all not publicly used APIs in &lt;code&gt;internal&lt;/code&gt; package. This was based on the publicly available extensions for k6 and may break private ones. More APIs will likely be removed or updated in follow-up releases after this more mechanical change.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/4292&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4292&lt;/a&gt; TypeScript is automatically supported and recognized if the script files use the &lt;code&gt;.ts&lt;/code&gt; extension. It also deprecates &lt;code&gt;experimental_enhanced&lt;/code&gt; compatibility mode as it is no longer necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;new-functional-testing-focused-official-jslib-k6-testing&#34;&gt;New functional testing focused official jslib &lt;a href=&#34;https://github.com/grafana/k6-jslib-testing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6-testing&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The k6 team has been developing a new official jslib dedicated to functional testing. While it is still under active development and will potentially see breaking changes, the set of APIs and behaviors it offers are meant to make their way into k6 eventually, and it is now available for early feedback.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/k6-jslib-testing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6-testing&lt;/a&gt; is a k6 JavaScript library that offers a seamless way to write functional tests in k6, using a Playwright-compatible assertions API. It exposes an &lt;code&gt;expect&lt;/code&gt; function, with which assertions can be performed using specific matchers that reflect the expected results.
Unlike current k6&amp;rsquo;s &lt;code&gt;check&lt;/code&gt; when &lt;code&gt;expects&lt;/code&gt; assertions fail, the test will immediately fail with a clear error message, including the expected and actual values in a similar fashion to what users would observe when using Playwright assertions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/k6-jslib-testing?tab=readme-ov-file#3-standard-assertions&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;There are many generic matchers&lt;/a&gt; (and more to come), such as &lt;code&gt;toEqual&lt;/code&gt;, &lt;code&gt;toBe&lt;/code&gt;, or &lt;code&gt;toBeTruthy&lt;/code&gt;, to only name a few, that can be used to assert conditions during a k6 test.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { expect } from &amp;#39;https://jslib.k6.io/k6-testing/0.6.1/index.js&amp;#39;;

export default function () {
  const response = http.get(&amp;#39;https://test.k6.io&amp;#39;);
  expect(response.status).toEqual(200);
  expect(response.body).toBeTruthy();
  expect(response.json()).toEqual(JSON.stringify({ message: &amp;#39;Hello, world!&amp;#39; }));
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/k6-jslib-testing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6-jslib-testing&lt;/a&gt; also includes browser-specific async matchers that wait until the expected condition is met such as &lt;code&gt;toBeVisible&lt;/code&gt;, &lt;code&gt;toBeDisabled&lt;/code&gt;, or &lt;code&gt;toBeChecked&lt;/code&gt;, to name a few.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { expect } from &amp;#39;https://jslib.k6.io/k6-testing/0.6.1/index.js&amp;#39;;
import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  try {
    // Navigate to the page
    await page.goto(&amp;#39;https://test.k6.io/my_messages.php&amp;#39;);

    // Type into the login input field: &amp;#39;testlogin&amp;#39;
    const loc = await page.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;);
    await loc.type(&amp;#39;testlogin&amp;#39;);

    // Assert that the login input field is visible
    await expect(page.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;)).toBeVisible();

    // Expecting this to fail as we have typed &amp;#39;testlogin&amp;#39; into the input instead of &amp;#39;foo&amp;#39;
    await expect(page.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;)).toHaveValue(&amp;#39;foo&amp;#39;);
  } finally {
    await page.close();
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It is currently available as part of the &lt;a href=&#34;https://jslib.k6.io&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;jslib.k6.io&lt;/a&gt; repository and is available to use in your k6 tests by adding the following import:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { expect } from &amp;#39;https://jslib.k6.io/k6-testing/0.6.1/index.js&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Try it out and give us feedback or contribute to the project on the &lt;a href=&#34;https://github.com/grafana/k6-jslib-testing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6-jslib-testing repository&lt;/a&gt;!&lt;/p&gt;
&lt;h3 id=&#34;--template-and---project-id-flags-for-k6-new-command-4153&#34;&gt;&lt;code&gt;--template&lt;/code&gt; and &lt;code&gt;--project-id&lt;/code&gt; flags for &lt;code&gt;k6 new&lt;/code&gt; command &lt;a href=&#34;https://github.com/grafana/k6/pull/4153&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4153&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;k6 new&lt;/code&gt; command has been revamped to provide an improved experience when scaffolding new k6 tests. It now supports a &lt;code&gt;--template&lt;/code&gt; flag with options such as &lt;code&gt;minimal&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;, and &lt;code&gt;browser&lt;/code&gt;, letting you generate a script tailored to your specific use case.&lt;/p&gt;
&lt;p&gt;The command also now accepts a &lt;code&gt;--project-id&lt;/code&gt; flag, which allows you to easily parameterize the test&amp;rsquo;s Grafana Cloud configuration.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;sh&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-sh&#34;&gt;# Create a new k6 script using the &amp;#39;protocol&amp;#39; template
$ k6 new --template protocol

# Create a Grafana k6 cloud-ready script with a specific project ID
$ k6 new --project-id 12345&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Refer to 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/test-authoring/create-test-script-using-the-cli/&#34;&gt;Create a test script using the CLI&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3 id=&#34;new-asobjects-option-in-k6experimentalcsv-module-4295&#34;&gt;New &lt;code&gt;asObjects&lt;/code&gt; option in &lt;code&gt;k6/experimental/csv&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/k6/pull/4295&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4295&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The CSV module&amp;rsquo;s parsing operations now support the &lt;code&gt;asObjects&lt;/code&gt; option, which enables parsing CSV data into JavaScript objects instead of arrays of strings (the default behavior).&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;asObjects&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;, the module parses CSV data into objects where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Column names from the header row become object keys.&lt;/li&gt;
&lt;li&gt;Column values become the corresponding object values.&lt;/li&gt;
&lt;li&gt;An error is thrown if no header row exists or if options modify the parsing start point.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the option set to &lt;code&gt;true&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;
import csv from &amp;#39;k6/experimental/csv&amp;#39;;

const csvData = csv.parse(&amp;#39;data.csv&amp;#39;, { asObjects: true });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;the following CSV file:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;csv&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-csv&#34;&gt;name,age,city
John,30,New York
Jane,25,Los Angeles&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;will be parsed into the following JavaScript objects:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;[
  { name: &amp;#39;John&amp;#39;, age: &amp;#39;30&amp;#39;, city: &amp;#39;New York&amp;#39; },
  { name: &amp;#39;Jane&amp;#39;, age: &amp;#39;25&amp;#39;, city: &amp;#39;Los Angeles&amp;#39; },
];&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Refer to the 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-experimental/csv/&#34;&gt;CSV module&amp;rsquo;s documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.57.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0570-release-notes">Version 0.57.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.57.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Introducing helpers for functional testing.&lt;/li>
&lt;li>The &lt;code>k6 new&lt;/code> command now supports templates and ProjectIDs.&lt;/li>
&lt;li>The &lt;code>k6/experimental/csv&lt;/code> module gets a new &lt;code>asObjects&lt;/code> option.&lt;/li>
&lt;li>We no longer support the &lt;code>k6/experimental/browser&lt;/code> module, in favor of &lt;code>k6/browser&lt;/code>.&lt;/li>
&lt;li>Moving most of non-public APIs to the &lt;code>internal&lt;/code> package.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/4161" target="_blank" rel="noopener noreferrer">#4161&lt;/a> Drops &lt;code>k6/experimental/browser&lt;/code>. If you&amp;rsquo;re still using it, follow the
&lt;a href="/docs/k6/v2.3.x/using-k6-browser/migrating-to-k6-v0-52/">instructions&lt;/a> to move to the graduated and stable &lt;code>k6/browser&lt;/code> module.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/4133" target="_blank" rel="noopener noreferrer">#4133&lt;/a> Moves all not publicly used APIs in &lt;code>internal&lt;/code> package. This was based on the publicly available extensions for k6 and may break private ones. More APIs will likely be removed or updated in follow-up releases after this more mechanical change.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/4292" target="_blank" rel="noopener noreferrer">#4292&lt;/a> TypeScript is automatically supported and recognized if the script files use the &lt;code>.ts&lt;/code> extension. It also deprecates &lt;code>experimental_enhanced&lt;/code> compatibility mode as it is no longer necessary.&lt;/li>
&lt;/ul>
&lt;h2 id="new-features">New features&lt;/h2>
&lt;h3 id="new-functional-testing-focused-official-jslib-k6-testing">New functional testing focused official jslib &lt;a href="https://github.com/grafana/k6-jslib-testing" target="_blank" rel="noopener noreferrer">k6-testing&lt;/a>&lt;/h3>
&lt;p>The k6 team has been developing a new official jslib dedicated to functional testing. While it is still under active development and will potentially see breaking changes, the set of APIs and behaviors it offers are meant to make their way into k6 eventually, and it is now available for early feedback.&lt;/p></description></item><item><title>Version 0.56.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.56.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.56.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0560-release-notes&#34;&gt;Version 0.56.0 release notes&lt;/h1&gt;
&lt;p&gt;k6 &lt;code&gt;v0.56.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We&amp;rsquo;ve merged xk6-browser into k6.&lt;/li&gt;
&lt;li&gt;Many small improvements, bug fixes and internal refactors.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1541&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1541&lt;/a&gt; Removes &lt;code&gt;accessibility-events&lt;/code&gt; from a test, which is no longer a valid permission that can be granted by the latest version of Chromium/Chrome.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/4093&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4093&lt;/a&gt; Unexports &lt;code&gt;lib/consts.FullVersion&lt;/code&gt; from the k6&amp;rsquo;s Golang API.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;merge-browser-code-in-k6-codebase-4056&#34;&gt;Merge browser code in k6 codebase &lt;a href=&#34;https://github.com/grafana/k6/pull/4056&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#4056&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While the browser module has been stabilized, the codebase was not moved inside of k6.&lt;/p&gt;
&lt;p&gt;As part of the stabilization this is now also merged in the k6 codebase. In the following months we would move issues from the &lt;a href=&#34;https://github.com/grafana/xk6-browser&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-browser&lt;/a&gt; repo and then archive it.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.56.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0560-release-notes">Version 0.56.0 release notes&lt;/h1>
&lt;p>k6 &lt;code>v0.56.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>We&amp;rsquo;ve merged xk6-browser into k6.&lt;/li>
&lt;li>Many small improvements, bug fixes and internal refactors.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/xk6-browser/pull/1541" target="_blank" rel="noopener noreferrer">browser#1541&lt;/a> Removes &lt;code>accessibility-events&lt;/code> from a test, which is no longer a valid permission that can be granted by the latest version of Chromium/Chrome.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/4093" target="_blank" rel="noopener noreferrer">#4093&lt;/a> Unexports &lt;code>lib/consts.FullVersion&lt;/code> from the k6&amp;rsquo;s Golang API.&lt;/li>
&lt;/ul>
&lt;h2 id="new-features">New features&lt;/h2>
&lt;h3 id="merge-browser-code-in-k6-codebase-4056">Merge browser code in k6 codebase &lt;a href="https://github.com/grafana/k6/pull/4056" target="_blank" rel="noopener noreferrer">#4056&lt;/a>&lt;/h3>
&lt;p>While the browser module has been stabilized, the codebase was not moved inside of k6.&lt;/p></description></item><item><title>Version 0.55.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.55.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.55.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0550-release-notes&#34;&gt;Version 0.55.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.55.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⚠️ The deprecated StatsD output has been removed.&lt;/li&gt;
&lt;li&gt;⚠️ The experimental &lt;code&gt;k6/experimental/tracing&lt;/code&gt; module has been removed.&lt;/li&gt;
&lt;li&gt;🆕 URL grouping support in the browser module.&lt;/li&gt;
&lt;li&gt;🆕 Top-level &lt;code&gt;await&lt;/code&gt; support.&lt;/li&gt;
&lt;li&gt;🔐 Complete RSA support for &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;h3 id=&#34;k6experimentaltracing-module-removed-3855&#34;&gt;&lt;code&gt;k6/experimental/tracing&lt;/code&gt; module removed &lt;a href=&#34;https://github.com/grafana/k6/pull/3855&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3855&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The experimental &lt;code&gt;k6/experimental/tracing&lt;/code&gt; module has been removed, in favor of a replacement &lt;a href=&#34;https://jslib.k6.io/http-instrumentation-tempo/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;jslib&lt;/a&gt; polyfill, please consult our &lt;a href=&#34;/docs/k6/latest/javascript-api/jslib/http-instrumentation-tempo/#migration-from-k6experimentaltracing&#34;&gt;guide on how to migrate&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/3855&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3855&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;statsd-output-removed-3849&#34;&gt;StatsD output removed &lt;a href=&#34;https://github.com/grafana/k6/pull/3849&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3849&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The StatsD output was deprecated in k6 v0.47.0 and is now removed. You could still output results to StatsD using the community xk6 extension &lt;a href=&#34;https://github.com/LeonAdato/xk6-output-statsd&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;LeonAdato/xk6-output-statsd&lt;/a&gt;. Thanks, @LeonAdato for taking over the extension!&lt;/p&gt;
&lt;h3 id=&#34;open-will-have-a-breaking-change-in-the-future&#34;&gt;&lt;code&gt;open&lt;/code&gt; will have a breaking change in the future.&lt;/h3&gt;
&lt;p&gt;Currently, &lt;code&gt;open&lt;/code&gt; opens relative files based on an unusual root, similar to how &lt;code&gt;require&lt;/code&gt; behaved before it was updated for ESM compatibility. To make k6 more consistent, &lt;code&gt;open&lt;/code&gt; and other functions like it will start handling relative paths in the same way as imports and &lt;code&gt;require&lt;/code&gt;.
For a more in-depth explanation, please refer to the related &lt;a href=&#34;https://github.com/grafana/k6/issues/3857&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With this version, k6 will start emitting warnings when it detects that in the future, this will break. We recommend using &lt;a href=&#34;/docs/k6/latest/javascript-api/import.meta/resolve/&#34;&gt;&lt;code&gt;import.meta.resolve()&lt;/code&gt;&lt;/a&gt; as a way to make your scripts future proof.&lt;/p&gt;
&lt;h3 id=&#34;httpfiledata-now-truly-has-the-same-type-as-the-provided-data-4009&#34;&gt;&lt;code&gt;http.file#data&lt;/code&gt; now truly has the same type as the provided data &lt;a href=&#34;https://github.com/grafana/k6/pull/4009&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#4009&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Previously &lt;code&gt;http.file#data&lt;/code&gt; was always a slice of byte (&lt;code&gt;[]byte&lt;/code&gt;) - which was very likely a bug and a leftover from years past.&lt;/p&gt;
&lt;p&gt;The original aim (also documented) was to have the same type as the data provided when creating the &lt;code&gt;http.file&lt;/code&gt; object, and it is now effectively the case.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;top-level-await-support-4007&#34;&gt;Top-level await support &lt;a href=&#34;https://github.com/grafana/k6/pull/4007&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;4007&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After the initial native support for ECMAScript modules, k6 can now load those modules asynchronously which also allows &lt;code&gt;await&lt;/code&gt; to be used in the top-level of a module. That is you can write &lt;code&gt;await someFunc()&lt;/code&gt; directly in the top most level of a module instead of having to make an async function that you call that can than use &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Until now, you had to wrap your code in an async function to use &lt;code&gt;await&lt;/code&gt; in the top-level of a module. For example, the following code:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { open } from &amp;#39;k6/experimental/fs&amp;#39;;
import csv from &amp;#39;k6/experimental/csv&amp;#39;;

let file;
let parser;
(async function () {
  file = await open(&amp;#39;data.csv&amp;#39;);
  parser = new csv.Parser(file);
})();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Can now be written as:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { open } from &amp;#39;k6/experimental/fs&amp;#39;;
import csv from &amp;#39;k6/experimental/csv&amp;#39;;

const file = await open(&amp;#39;data.csv&amp;#39;);
const parser = new csv.Parser(file);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This should make using the increasing number of async APIs in k6 easier in the &lt;a href=&#34;/docs/k6/latest/using-k6/test-lifecycle/#the-init-stage&#34;&gt;init context&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is not allowed in case of using the CommonJS modules, only ECMAScript modules, as CommonJS modules are synchronous by definition.&lt;/p&gt;
&lt;h3 id=&#34;complete-rsa-support-for-k6experimentalwebcrypto-4025&#34;&gt;Complete&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; RSA support for &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/4025&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#4025&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This update includes support for the RSA family of algorithms, including &lt;code&gt;RSA-OAEP&lt;/code&gt;, &lt;code&gt;RSA-PSS&lt;/code&gt; and &lt;code&gt;RSASSA-PKCS1-v1_5&lt;/code&gt;. You can use these algorithms with the &lt;code&gt;crypto.subtle&lt;/code&gt; API in the same way as the other algorithms, precisely for &lt;code&gt;generateKey&lt;/code&gt;, &lt;code&gt;importKey&lt;/code&gt;, &lt;code&gt;exportKey&lt;/code&gt;, &lt;code&gt;encrypt&lt;/code&gt;, &lt;code&gt;decrypt&lt;/code&gt;, &lt;code&gt;sign&lt;/code&gt;, and &lt;code&gt;verify&lt;/code&gt; operations.&lt;/p&gt;
&lt;p&gt;By implementing RSA support, we make our WebCrypto API implementation more complete and useful for a broader range of use cases.&lt;/p&gt;
&lt;h4 id=&#34;example-usage&#34;&gt;Example usage&lt;/h4&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { crypto } from &amp;#39;k6/experimental/webcrypto&amp;#39;;

export default async function () {
  const keyPair = await crypto.subtle.generateKey(
    {
      name: &amp;#39;RSA-PSS&amp;#39;,
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: { name: &amp;#39;SHA-1&amp;#39; },
    },
    true,
    [&amp;#39;sign&amp;#39;, &amp;#39;verify&amp;#39;]
  );

  console.log(JSON.stringify(keyPair));
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;pageonmetric-to-group-urls-browser371-browser1487&#34;&gt;&lt;code&gt;page.on(&#39;metric)&lt;/code&gt; to group urls &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/371&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#371&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/1487&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1487&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modern websites are complex and make a high number of requests to function as intended by their developers. These requests no longer serve only content for display to the end user but also retrieve insights, analytics, advertisements, and for cache-busting purposes. Such requests are usually generated dynamically and may contain frequently changing IDs, posing challenges when correlating and analyzing your k6 test results.&lt;/p&gt;
&lt;p&gt;When load testing a website using the k6 browser module, these dynamic requests can result in a high number of similar-looking requests, making it difficult to correlate them and extract valuable insights. This can also lead to test errors, such as a &amp;ldquo;too-many-metrics&amp;rdquo; error, due to high cardinality from metrics tagged with similar but dynamically changing URLs.&lt;/p&gt;
&lt;p&gt;This issue also affects synthetic tests. While you may not encounter the &amp;ldquo;too-many-metrics&amp;rdquo; error, you may end up with a large amount of uncorrelated metric data that cannot be tracked effectively over time.&lt;/p&gt;
&lt;p&gt;To address this in the browser module, we have implemented &lt;code&gt;page.on(&#39;metric&#39;)&lt;/code&gt;, which allows you to define URL patterns using regex for matching. When a match is found, the URL and name tags for the metric are replaced with the new name.&lt;/p&gt;
&lt;h4 id=&#34;example-usage-1&#34;&gt;Example usage&lt;/h4&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import { browser } from &amp;#39;k6/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();

  // Here, we set up an event listener using page.on(&amp;#39;metric&amp;#39;).
  // You can call page.on(&amp;#39;metric&amp;#39;) multiple times, and each callback function
  // will be executed in the order that page.on was called.
  page.on(&amp;#39;metric&amp;#39;, (metric) =&amp;gt; {
    // Currently, metric.tag is the only available method on the metric object.
    // It enables matching on the URL tag using a specified regex pattern.
    // You can call metric.tag multiple times within the callback function.
    metric.tag({
      // This is the new name assigned to any metric that matches the defined
      // URL pattern below.
      name: &amp;#39;test&amp;#39;,
      // Provide one or more match patterns here. Any metrics that match a pattern
      // will use the new name specified above.
      matches: [
        // Each match pattern can include a URL and an optional method.
        // When a method is specified, the metric must match both the URL pattern
        // and the method. If no method is provided, the pattern will match all
        // HTTP methods.
        { url: /^https:\/\/test\.k6\.io\/\?q=[0-9a-z]&amp;#43;$/, method: &amp;#39;GET&amp;#39; },
      ],
    });
  });

  try {
    // The following lines are for demonstration purposes.
    // Visiting URLs with different query parameters (q) to illustrate matching.
    await page.goto(&amp;#39;https://test.k6.io/?q=abc123&amp;#39;);
    await page.goto(&amp;#39;https://test.k6.io/?q=def456&amp;#39;);
  } finally {
    // Ensure the page is closed after testing.
    await page.close();
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;controlormeta-support-in-the-keyboard-browser1457&#34;&gt;&lt;code&gt;ControlOrMeta&lt;/code&gt; support in the keyboard &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1457&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1457&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This approach enables tests to be written for all platforms, accommodating either &lt;code&gt;Control&lt;/code&gt; or &lt;code&gt;Meta&lt;/code&gt; for keyboard actions. For example, &lt;code&gt;Control&#43;click&lt;/code&gt; on Windows and &lt;code&gt;Meta&#43;click&lt;/code&gt;on Mac to open a link in a new window.&lt;/p&gt;
&lt;h4 id=&#34;example-usage-2&#34;&gt;Example usage&lt;/h4&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;await page.keyboard.down(&amp;#39;ControlOrMeta&amp;#39;);

// Open the link in a new tab.
// Wait for the new page to be created.
const browserContext = browser.context();
const [newTab] = await Promise.all([
  browserContext.waitForEvent(&amp;#39;page&amp;#39;),
  await page.locator(&amp;#39;a[href=&amp;#34;/my_messages.php&amp;#34;]&amp;#39;).click(),
]);

await page.keyboard.up(&amp;#39;ControlOrMeta&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.55.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Since under the hood we do fully rely on the Golang&amp;rsquo;s SDK, our implementation doesn&amp;rsquo;t support zero salt lengths for the &lt;code&gt;RSA-PSS&lt;/code&gt; &lt;code&gt;sign&lt;/code&gt;/&lt;code&gt;verify&lt;/code&gt; operations.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="version-0550-release-notes">Version 0.55.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.55.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>⚠️ The deprecated StatsD output has been removed.&lt;/li>
&lt;li>⚠️ The experimental &lt;code>k6/experimental/tracing&lt;/code> module has been removed.&lt;/li>
&lt;li>🆕 URL grouping support in the browser module.&lt;/li>
&lt;li>🆕 Top-level &lt;code>await&lt;/code> support.&lt;/li>
&lt;li>🔐 Complete RSA support for &lt;code>k6/experimental/webcrypto&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;h3 id="k6experimentaltracing-module-removed-3855">&lt;code>k6/experimental/tracing&lt;/code> module removed &lt;a href="https://github.com/grafana/k6/pull/3855" target="_blank" rel="noopener noreferrer">&lt;code>#3855&lt;/code>&lt;/a>&lt;/h3>
&lt;p>The experimental &lt;code>k6/experimental/tracing&lt;/code> module has been removed, in favor of a replacement &lt;a href="https://jslib.k6.io/http-instrumentation-tempo/" target="_blank" rel="noopener noreferrer">jslib&lt;/a> polyfill, please consult our &lt;a href="/docs/k6/latest/javascript-api/jslib/http-instrumentation-tempo/#migration-from-k6experimentaltracing">guide on how to migrate&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/3855" target="_blank" rel="noopener noreferrer">&lt;code>#3855&lt;/code>&lt;/a>.&lt;/p></description></item><item><title>Version 0.54.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.54.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.54.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0540-release-notes&#34;&gt;Version 0.54.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.54.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new experimental CSV module&lt;/li&gt;
&lt;li&gt;New &lt;code&gt;k6 cloud&lt;/code&gt; commands for local execution and uploading script files&lt;/li&gt;
&lt;li&gt;New ECMAScript features&lt;/li&gt;
&lt;li&gt;Updated logo and branding&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3913&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3913&lt;/a&gt; changes the mapping of Golang&amp;rsquo;s &lt;code&gt;math/big.Int&lt;/code&gt; type to &lt;code&gt;bigint&lt;/code&gt; type in k6.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3922&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3922&lt;/a&gt; removes &lt;code&gt;lib.Min&lt;/code&gt; and &lt;code&gt;lib.Max&lt;/code&gt; from k6&amp;rsquo;s Go API, which could affect custom extensions that rely on these functions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3838&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3838&lt;/a&gt; removes &lt;code&gt;k6/experimental/timers&lt;/code&gt; - they are now available globally and no import is needed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3944&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3944&lt;/a&gt; updates to &lt;code&gt;k6/experimental/websockets&lt;/code&gt;, which makes the &lt;code&gt;binaryType&lt;/code&gt; default value equal to &lt;code&gt;&amp;quot;blob&amp;quot;&lt;/code&gt;. With this change, &lt;code&gt;k6/experimental/websockets&lt;/code&gt; is now compliant with the specification.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;branding-changes-and-logo-3946-3953-3969&#34;&gt;Branding changes and logo &lt;a href=&#34;https://github.com/grafana/k6/pull/3946&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3946&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/3953&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3953&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/3969&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3969&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As part of joining Grafana Labs in 2021, k6 was renamed to Grafana k6. The original k6 logo and branding was purple, which didn&amp;rsquo;t fit very well next to the Grafana Labs orange logo and all its other products.&lt;/p&gt;
&lt;p&gt;In this release, we have a new logo in a new color, and the terminal banner has been redesigned to match the current branding more closely.&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;
  &lt;img src=&#34;https://github.com/grafana/k6/blob/31e3db711850fdd270e56e53feb3a2e4209e542a/assets/logo.svg&#34; alt=&#34;Grafana k6 logo&#34; width=&#34;210&#34;&gt;
&lt;/p&gt;
&lt;h3 id=&#34;new-experimental-csv-module-for-efficient-csv-data-handling-3743&#34;&gt;New experimental CSV module for efficient CSV data handling &lt;a href=&#34;https://github.com/grafana/k6/pull/3743&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3743&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We’ve added a new experimental CSV module to k6 for more efficient and convenient CSV parsing and streaming, addressing the limitations of preexisting JavaScript-based solutions like &lt;a href=&#34;https://www.papaparse.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;papaparse&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;what-is-it&#34;&gt;What is it?&lt;/h4&gt;
&lt;p&gt;The CSV module offers two key features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;csv.parse()&lt;/code&gt;: This function parses a CSV file into a &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-data/sharedarray/&#34;&gt;SharedArray&lt;/a&gt; at once using Go-based processing for faster parsing and lower memory usage compared to JavaScript alternatives.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;csv.Parser&lt;/code&gt;: This class provides a streaming parser to read CSV files line-by-line, minimizing memory consumption and offering more control over parsing through a stream-like API. This is ideal for scenarios where memory optimization or fine-grained control of the parsing process is crucial.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;benefits-for-users&#34;&gt;Benefits for users&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Faster Parsing&lt;/strong&gt;: &lt;code&gt;csv.parse&lt;/code&gt; bypasses most of the JavaScript runtime, offering significant speed improvements for large files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lower Memory Usage&lt;/strong&gt;: Both solutions support shared memory across virtual users (VUs) with the &lt;code&gt;fs.open&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: Choose between full-file parsing with &lt;code&gt;csv.parse()&lt;/code&gt; or memory-efficient streaming with &lt;code&gt;csv.Parser&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;tradeoffs&#34;&gt;Tradeoffs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;csv.Parse&lt;/code&gt;&lt;/strong&gt;: Parses the entire file in the initialization phase of the test, which can increase startup time and memory usage for large files. Best suited for scenarios where performance is prioritized over memory consumption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;csv.Parser&lt;/code&gt;&lt;/strong&gt;: Reads the file line-by-line, making it more memory-efficient but potentially slower due to reading overhead for each line. Ideal for scenarios where memory usage is a concern or where fine-grained control over parsing is needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;example-usage&#34;&gt;Example usage&lt;/h4&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of Parsing a full CSV file into a SharedArray.&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { open } from &amp;#39;k6/experimental/fs&amp;#39;;
import csv from &amp;#39;k6/experimental/csv&amp;#39;;
import { scenario } from &amp;#39;k6/execution&amp;#39;;

export const options = {
  iterations: 10,
};

let file;
let csvRecords;
(async function () {
  file = await open(&amp;#39;data.csv&amp;#39;);

  // The `csv.parse` function consumes the entire file at once, and returns
  // the parsed records as a SharedArray object.
  csvRecords = await csv.parse(file, { delimiter: &amp;#39;,&amp;#39; });
})();

export default async function () {
  // The csvRecords a SharedArray. Each element is a record from the CSV file, represented as an array
  // where each element is a field from the CSV record.
  //
  // Thus, `csvRecords[scenario.iterationInTest]` will give us the record for the current iteration.
  console.log(csvRecords[scenario.iterationInTest]);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of streaming a CSV file line-by-line.&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { open } from &amp;#39;k6/experimental/fs&amp;#39;;
import csv from &amp;#39;k6/experimental/csv&amp;#39;;

export const options = {
  iterations: 10,
};

let file;
let parser;
(async function () {
  file = await open(&amp;#39;data.csv&amp;#39;);
  parser = new csv.Parser(file);
})();

export default async function () {
  // The parser `next` method attempts to read the next row from the CSV file.
  //
  // It returns an iterator-like object with a `done` property that indicates whether
  // there are more rows to read, and a `value` property that contains the row fields
  // as an array.
  const { done, value } = await parser.next();
  if (done) {
    throw new Error(&amp;#39;No more rows to read&amp;#39;);
  }

  // We expect the `value` property to be an array of strings, where each string is a field
  // from the CSV record.
  console.log(done, value);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&#34;new-k6-cloud-run---local-execution-flag-for-local-execution-of-cloud-tests-3904-and-3931&#34;&gt;New &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; flag for local execution of cloud tests &lt;a href=&#34;https://github.com/grafana/k6/pull/3904&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3904&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&#34;https://github.com/grafana/k6/pull/3931&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3931&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release introduces the &lt;code&gt;--local-execution&lt;/code&gt; flag for the k6 cloud run command, allowing you to run test executions locally while sending metrics to Grafana Cloud k6.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;k6 cloud run --local-execution script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;By default, using the &lt;code&gt;--local-execution&lt;/code&gt; flag uploads the test archive to Grafana Cloud k6. If you want to disable this upload, use the &lt;code&gt;--no-archive-upload&lt;/code&gt; flag.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;--local-execution&lt;/code&gt; flag currently functions similarly to the &lt;code&gt;k6 run -o cloud&lt;/code&gt; command, which is now considered deprecated (though it is not planned to be removed). Future updates will enhance &lt;code&gt;--local-execution&lt;/code&gt; with additional capabilities that the &lt;code&gt;k6 run -o cloud&lt;/code&gt; command does not offer.&lt;/p&gt;
&lt;h3 id=&#34;new-k6-cloud-upload-command-for-uploading-test-files-to-the-cloud-3906&#34;&gt;New &lt;code&gt;k6 cloud upload&lt;/code&gt; command for uploading test files to the cloud &lt;a href=&#34;https://github.com/grafana/k6/pull/3906&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3906&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We continue to refine and improve the cloud service to improve how we handle uploading test files, so we&amp;rsquo;ve added a new &lt;code&gt;k6 cloud upload&lt;/code&gt; command that replaces the &lt;code&gt;k6 cloud --upload-only&lt;/code&gt; flag, which is now considered deprecated.&lt;/p&gt;
&lt;h3 id=&#34;grpc-module-updates-driven-by-contributors&#34;&gt;gRPC module updates driven by contributors&lt;/h3&gt;
&lt;h4 id=&#34;new-discardresponsemessage-option&#34;&gt;New &lt;code&gt;discardResponseMessage&lt;/code&gt; option&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3877&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3877&lt;/a&gt; and &lt;a href=&#34;https://github.com/grafana/k6/pull/3820&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3820&lt;/a&gt; add a new option for the gRPC module &lt;code&gt;discardResponseMessage&lt;/code&gt;, which allows users to discard the messages received from the server.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const resp = client.invoke(&amp;#39;main.RouteGuide/GetFeature&amp;#39;, req, { discardResponseMessage: true });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This reduces the amount of memory required and the amount of garbage collection, which reduces the load on the testing machine and can help produce more reliable test results.&lt;/p&gt;
&lt;p&gt;Thank you, @lzakharov!&lt;/p&gt;
&lt;h4 id=&#34;new-argument-meta-for-grpcs-stream-callbacks&#34;&gt;New argument &lt;code&gt;meta&lt;/code&gt; for gRPC&amp;rsquo;s stream callbacks&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3801&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3801&lt;/a&gt; adds a new argument &lt;code&gt;meta&lt;/code&gt; to gRPC&amp;rsquo;s stream callback, which handles the timestamp of the original event (for example, when a message has been received).&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;let stream = new grpc.Stream(client, &amp;#39;main.FeatureExplorer/ListFeatures&amp;#39;);
stream.on(&amp;#39;data&amp;#39;, function (data, meta) {
  // will print the timestamp when message has been received
  call(meta.ts);
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thank you, @cchamplin!&lt;/p&gt;
&lt;h4 id=&#34;allow-missing-file-descriptors-for-grpc-reflection&#34;&gt;Allow missing file descriptors for gRPC reflection&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3871&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3871&lt;/a&gt; allows missing file descriptors for gRPC reflection.&lt;/p&gt;
&lt;p&gt;Thank you, @Lordnibbler!&lt;/p&gt;
&lt;h3 id=&#34;sobek-updates-brings-support-of-new-ecmascript-features-into-k6-3899-3925-3913&#34;&gt;Sobek updates brings support of new ECMAScript features into k6 &lt;a href=&#34;https://github.com/grafana/k6/pull/3899&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3899&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/3925&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3925&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/3913&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3913&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this release, we&amp;rsquo;ve updated &lt;a href=&#34;https://github.com/grafana/sobek&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Sobek&lt;/a&gt; (the &lt;code&gt;ECMAScript&lt;/code&gt; implementation in Go) which contains the new ECMAScript features that are now available in k6.&lt;/p&gt;
&lt;p&gt;This includes support for numeric literal separators:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const billion = 1_000_000_000;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Support for &lt;code&gt;BigInt&lt;/code&gt;, the values which are too large to be represented by the number primitive:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const huge = BigInt(9007199254740991);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Note: Before k6 version v0.54, Golang&amp;rsquo;s type &lt;code&gt;math/big.Int&lt;/code&gt; mapped to another type, so this might be a breaking change for some extensions or users.&lt;/p&gt;
&lt;p&gt;RegExp dotAll support, where you can match newline characters with &lt;code&gt;.&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const str1 = &amp;#39;bar\nexample foo example&amp;#39;;

const regex1 = /bar.example/s;

console.log(regex1.dotAll); // true&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Support for ES2023 Array methods: &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/with&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;with&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;toSpliced&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;toReversed&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;toSorted&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you @shiroyk for adding both the new array methods and BitInt :bow:.&lt;/p&gt;
&lt;h3 id=&#34;new-setchecked-method-for-the-browser-module-browser1403&#34;&gt;New &lt;code&gt;setChecked&lt;/code&gt; method for the browser module &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1403&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;browser#1403&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Previously, users could check or uncheck checkbox and radio button elements using the &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/check/&#34;&gt;&lt;code&gt;check&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/uncheck/&#34;&gt;&lt;code&gt;uncheck&lt;/code&gt;&lt;/a&gt; methods. Now, we&amp;rsquo;ve added a &lt;code&gt;setChecked&lt;/code&gt; method that allows users to set a checkbox or radio button to either the checked or unchecked state with a single method and a boolean argument.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;await page.setChecked(&amp;#39;#checkbox&amp;#39;, true); // check the checkbox
await page.setChecked(&amp;#39;#checkbox&amp;#39;, false); // uncheck the checkbox&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/page/setchecked/&#34;&gt;Page&lt;/a&gt;, &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/frame/setchecked/&#34;&gt;Frame&lt;/a&gt;, &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/elementhandle/setchecked/&#34;&gt;ElementHandle&lt;/a&gt;, and &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/locator/setchecked/&#34;&gt;Locator&lt;/a&gt; now support the new &lt;code&gt;setChecked&lt;/code&gt; method.&lt;/p&gt;
&lt;h3 id=&#34;async-check-function-utility-k6-utils13&#34;&gt;Async &lt;code&gt;check&lt;/code&gt; function utility &lt;a href=&#34;https://github.com/grafana/k6-jslib-utils/pull/13&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;k6-utils#13&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Writing concise code can be difficult when using &lt;a href=&#34;/docs/k6/latest/javascript-api/k6/check/&#34;&gt;the k6 &lt;code&gt;check&lt;/code&gt; function&lt;/a&gt; with async code since it doesn&amp;rsquo;t support async APIs. A solution that we have suggested so far is to declare a temporary variable, wait for the value that is to be checked, and then check the result later. However, this approach can clutter the code with single-use declarations and unnecessary variable names, for example:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const checked = await p.locator(&amp;#39;.checked&amp;#39;).isChecked();

check(checked, {
  checked: (c) =&amp;gt; c,
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To address this limitation, we&amp;rsquo;ve added a version of the &lt;code&gt;check&lt;/code&gt; function to &lt;a href=&#34;https://jslib.k6.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;jslib.k6.io&lt;/a&gt; that makes working with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; simpler. The &lt;code&gt;check&lt;/code&gt; function is a drop-in replacement for the built-in check, with added support for async code. Any &lt;code&gt;Promise&lt;/code&gt;s will be awaited, and the result is reported once the operation has been completed:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Import the new check function from jslib.k6.io/k6-utils
import { check } from &amp;#39;https://jslib.k6.io/k6-utils/1.5.0/index.js&amp;#39;;

// ...

// Use the new check function with async code
check(page, {
  checked: async (p) =&amp;gt; p.locator(&amp;#39;.checked&amp;#39;).isChecked(),
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Check out &lt;a href=&#34;/docs/k6/latest/javascript-api/jslib/utils/check/&#34;&gt;the &lt;code&gt;check&lt;/code&gt; utility function&amp;rsquo;s documentation&lt;/a&gt; for more information on how to use it.&lt;/p&gt;
&lt;h3 id=&#34;k6experimnetalwebsockets-updates-towards-websockets-api-compatibility&#34;&gt;&lt;code&gt;k6/experimnetal/websockets&lt;/code&gt; updates towards WebSockets API compatibility&lt;/h3&gt;
&lt;h4 id=&#34;support-arraybufferviews-in-send-for-k6experimentalwebsockets-3944&#34;&gt;Support ArrayBufferViews in &lt;code&gt;send&lt;/code&gt; for &lt;code&gt;k6/experimental/websockets&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/3844&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3944&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As part of making &lt;code&gt;k6/experimental/websockets&lt;/code&gt; compliant with the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/WebSocket&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;WebSocket API&lt;/a&gt;, it now supports Uint8Array and other ArrayBufferViews directly as arguments to &lt;code&gt;send&lt;/code&gt;, instead of having to specifically provide their &lt;code&gt;buffer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This should make the module more compliant with libraries that use the WebSocket API.&lt;/p&gt;
&lt;p&gt;Thanks to @pixeldrew for &lt;a href=&#34;https://github.com/grafana/xk6-websockets/issues/75&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;reporting&lt;/a&gt; this. :bow:&lt;/p&gt;
&lt;h4 id=&#34;readystate-actually-being-a-number-3972&#34;&gt;&lt;code&gt;readyState&lt;/code&gt; actually being a number &lt;a href=&#34;https://github.com/grafana/k6/pull/3972&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3972&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Due to goja/Sobek internal workings, &lt;code&gt;readyState&lt;/code&gt; wasn&amp;rsquo;t exactly a number in JavaScript code. This had some abnormal behavior, which limited interoperability with libraries.&lt;/p&gt;
&lt;p&gt;This has been fixed, and &lt;code&gt;readyState&lt;/code&gt; is a regular number from the JavaScript perspective.&lt;/p&gt;
&lt;p&gt;Thanks to @dougw-bc for &lt;a href=&#34;https://github.com/grafana/xk6-websockets/issues/79&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;reporting&lt;/a&gt; this. :bow:&lt;/p&gt;
&lt;h3 id=&#34;rework-of-how-usage-is-being-collected-internally-and-additional-counters-3917-and-3951&#34;&gt;Rework of how usage is being collected internally and additional counters &lt;a href=&#34;https://github.com/grafana/k6/pull/3917&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3917&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://github.com/grafana/k6/pull/3951&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;#3951&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As part of working on k6 over the years, we have often wondered if users use certain features or if they see a strange corner case behavior.&lt;/p&gt;
&lt;p&gt;We have usually made guesses or tried to extrapolate from experience on issues we see. Unfortunately, this isn&amp;rsquo;t always easy or possible, and it&amp;rsquo;s definitely very skewed. As such, we usually also add warning messages to things we intend to break to inform people and ask them to report problems. But we usually see very little activity, especially before we make changes.&lt;/p&gt;
&lt;p&gt;This also only works for things we want to remove, and it doesn&amp;rsquo;t help us know if people use new functionality at all or if there are patterns we don&amp;rsquo;t expect.&lt;/p&gt;
&lt;p&gt;While k6 has been collecting usage for a while, they&amp;rsquo;re surface-level things, such as how many VUs were run, the k6 version, or which &lt;em&gt;internal&lt;/em&gt; modules were loaded. The system for this was also very rigid, requiring a lot of work to add simple things, such as if someone used the &lt;code&gt;require&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;This process has been reworked to make things easier, and a few new usage reports have been added:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When Grafana Cloud is used, it will also tell us which test run it is. We already have most of the information reported through metrics and from the test being executed in the cloud. But this will help us filter cloud test runs from local runs and potentially warn cloud users if they&amp;rsquo;re using experimental modules that will be removed.&lt;/li&gt;
&lt;li&gt;The number of files parsed, as well as the number of .ts files parsed. This will help us understand if people use small or big projects and if TypeScript support is being used.&lt;/li&gt;
&lt;li&gt;Usage of &lt;code&gt;require&lt;/code&gt;. Now that we have ESM native support, using &lt;code&gt;require&lt;/code&gt; and CommonJS adds complexity. It&amp;rsquo;s interesting to us whether removing this in the future - likely years, given its support in other runtimes, is feasible.&lt;/li&gt;
&lt;li&gt;Usage of &lt;code&gt;global&lt;/code&gt;. This will help us decide if we can &lt;a href=&#34;https://github.com/grafana/k6/issues/3864&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;drop compatibility-mode&lt;/a&gt; differences or even the whole concept.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.54.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0540-release-notes">Version 0.54.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.54.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>A new experimental CSV module&lt;/li>
&lt;li>New &lt;code>k6 cloud&lt;/code> commands for local execution and uploading script files&lt;/li>
&lt;li>New ECMAScript features&lt;/li>
&lt;li>Updated logo and branding&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3913" target="_blank" rel="noopener noreferrer">#3913&lt;/a> changes the mapping of Golang&amp;rsquo;s &lt;code>math/big.Int&lt;/code> type to &lt;code>bigint&lt;/code> type in k6.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3922" target="_blank" rel="noopener noreferrer">#3922&lt;/a> removes &lt;code>lib.Min&lt;/code> and &lt;code>lib.Max&lt;/code> from k6&amp;rsquo;s Go API, which could affect custom extensions that rely on these functions.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3838" target="_blank" rel="noopener noreferrer">#3838&lt;/a> removes &lt;code>k6/experimental/timers&lt;/code> - they are now available globally and no import is needed.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3944" target="_blank" rel="noopener noreferrer">#3944&lt;/a> updates to &lt;code>k6/experimental/websockets&lt;/code>, which makes the &lt;code>binaryType&lt;/code> default value equal to &lt;code>&amp;quot;blob&amp;quot;&lt;/code>. With this change, &lt;code>k6/experimental/websockets&lt;/code> is now compliant with the specification.&lt;/li>
&lt;/ul>
&lt;h2 id="new-features">New features&lt;/h2>
&lt;h3 id="branding-changes-and-logo-3946-3953-3969">Branding changes and logo &lt;a href="https://github.com/grafana/k6/pull/3946" target="_blank" rel="noopener noreferrer">&lt;code>#3946&lt;/code>&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/3953" target="_blank" rel="noopener noreferrer">&lt;code>#3953&lt;/code>&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/3969" target="_blank" rel="noopener noreferrer">&lt;code>#3969&lt;/code>&lt;/a>&lt;/h3>
&lt;p>As part of joining Grafana Labs in 2021, k6 was renamed to Grafana k6. The original k6 logo and branding was purple, which didn&amp;rsquo;t fit very well next to the Grafana Labs orange logo and all its other products.&lt;/p></description></item><item><title>Version 0.53.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.53.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.53.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0530-release-notes&#34;&gt;Version 0.53.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.53.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Native ECMAScript modules support&lt;/li&gt;
&lt;li&gt;New experimental OpenTelemetry metrics output&lt;/li&gt;
&lt;li&gt;Blob support in experimental websockets module&lt;/li&gt;
&lt;li&gt;Consolidate cloud features and commands under &lt;code&gt;k6 cloud&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Breaking change: remove magic URL resolutions&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;h3 id=&#34;require-is-now-specification-compliant-and-always-resolves-based-on-the-file-it-is-written-in-3534&#34;&gt;Require is now specification compliant and always resolves based on the file it is written in &lt;a href=&#34;https://github.com/grafana/k6/issues/3534&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3534&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;require&lt;/code&gt; function in k6 used to resolve identifiers based on the current &amp;ldquo;root of execution&amp;rdquo; (more on that later). In a lot of cases, that aligns with the file the &lt;code&gt;require&lt;/code&gt; is written in or a file in the same folder, which leads to the same result. In a small subset of cases, this isn&amp;rsquo;t the case.&lt;/p&gt;
&lt;p&gt;In every other implementation, and more or less by the CommonJS specification, &lt;code&gt;require&lt;/code&gt; should always be relative to the file it is written in.&lt;/p&gt;
&lt;p&gt;This also aligns with how ESM and dynamic &lt;code&gt;import&lt;/code&gt; also work. In order to align with them &lt;code&gt;require&lt;/code&gt; now uses the same underlying implementation.&lt;/p&gt;
&lt;p&gt;There was a warning message for the last 2 releases trying to tease out cases where that would be problematic.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;&#34;root of execution&#34; explanation&lt;/summary&gt;
&lt;p&gt;This is very much an implementation detail that has leaked and likely a not intended one.&lt;/p&gt;
&lt;p&gt;Whenever a file is &lt;code&gt;require&lt;/code&gt;-ed it becomes the &amp;ldquo;root of execution&amp;rdquo;, and both &lt;code&gt;require&lt;/code&gt; and &lt;code&gt;open&lt;/code&gt; become relative to it. Once the &lt;code&gt;require&lt;/code&gt; finishes, the previous &amp;ldquo;root of execution&amp;rdquo; gets restored. Outside of the &lt;code&gt;init&lt;/code&gt; context execution, the main file is the &amp;ldquo;root of execution&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;p&gt;Have 3 files:
main.js&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const s = require(&amp;#39;./A/a.js&amp;#39;);
if (s() != 5) {
  throw &amp;#39;Bad&amp;#39;;
}
module.exports.default = () =&amp;gt; {}; // just for k6 to not error&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;/A/a.js:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;module.exports = function () {
  return require(&amp;#39;./b.js&amp;#39;);
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;/A/b.js&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;module.exports = 5;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example when &lt;code&gt;require&lt;/code&gt; is called in &lt;code&gt;/A/a.js&lt;/code&gt; the &lt;code&gt;main.js&lt;/code&gt; is once again the &amp;ldquo;root of execution&amp;rdquo;. If you call the function in &lt;code&gt;/A/a.js&lt;/code&gt; just after defining it though, it will work as expected.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;You can use the newly added &lt;code&gt;import.meta.resolve()&lt;/code&gt; function if you want to create a path that is relevant to the currently calling module. That will let you call it outside of a helper class and provide the path to it. Refer to &lt;a href=&#34;/docs/k6/latest/javascript-api/import.meta/resolve/&#34;&gt;docs&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3 id=&#34;ecmascript-modules-esm-native-support-related-breaking-changes&#34;&gt;ECMAScript Modules (ESM) Native Support related breaking changes&lt;/h3&gt;
&lt;p&gt;As part of the ESM native support implementation, two common broken patterns in the ecosystem became apparent.&lt;/p&gt;
&lt;p&gt;One is arguably a developer experience improvement, and the other is a consequence of the previous implementation.&lt;/p&gt;
&lt;h4 id=&#34;mixing-commonjs-and-esm&#34;&gt;Mixing CommonJS and ESM&lt;/h4&gt;
&lt;p&gt;Previously, k6 used a transpiler (Babel) internally to transpile ESM syntax to CommonJS. That led to all code always being CommonJS, and if you had CommonJS next to it, Babel would not complain.&lt;/p&gt;
&lt;p&gt;As k6 (or the underlying JS VM implementation) did not understand ESM in itself and that CommonJS is a 100% during execution feature, this was not easy to detect or prevent.&lt;/p&gt;
&lt;p&gt;We added a &lt;a href=&#34;https://github.com/grafana/k6/pull/3807&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;warning&lt;/a&gt; in v0.52.0 to give users time for migration.&lt;/p&gt;
&lt;p&gt;To fix this - all you need is to stick to either CommonJS or ESM within each file.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;Code examples and proposed changes&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { sleep } from &amp;#34;k6&amp;#34;;

module.exports.default = func() { ...}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In the example above both ESM and CommonJS are used in the same file.&lt;/p&gt;
&lt;p&gt;You can either replace:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;module.exports.default = func() {}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With the ESM syntax:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;export default func() {}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Or replace:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { sleep } from &amp;#39;k6&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With CommonJS:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const sleep = require(&amp;#39;k6&amp;#39;).sleep;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h4 id=&#34;imported-identifier-that-cant-be-resolved-are-now-errors&#34;&gt;Imported identifier that can&amp;rsquo;t be resolved are now errors&lt;/h4&gt;
&lt;p&gt;Previous to this, if you were using the ESM syntax and imported the &lt;code&gt;foo&lt;/code&gt; identifier, but the exporting file didn&amp;rsquo;t export it, there wouldn&amp;rsquo;t be an error.&lt;/p&gt;
&lt;p&gt;bar.js:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;export const notfoo = 5;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;main.js&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { foo } from &amp;#39;./bar.js&amp;#39;;
export default function () {
  foo.bar(); // throws exception here
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The example would not error out, but when it is accessed, there would be an exception as &lt;code&gt;foo&lt;/code&gt; would be &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With native ESM support, that is an error as defined by the specification and will occur sooner.&lt;/p&gt;
&lt;p&gt;This arguably improves UX/DX, but we have reports that some users have imports like this but do not use them. So, they wouldn&amp;rsquo;t be getting exceptions, but they would now get errors.&lt;/p&gt;
&lt;p&gt;The solution, in this case, is to stop importing the not exported identifiers.&lt;/p&gt;
&lt;h3 id=&#34;no-more-magic-url-resolution&#34;&gt;No more &amp;ldquo;magic&amp;rdquo; URL resolution&lt;/h3&gt;
&lt;p&gt;For a long time, k6 has supported special &lt;em&gt;magic&lt;/em&gt; URLs that aren&amp;rsquo;t really that.&lt;/p&gt;
&lt;p&gt;Those were URLs without a scheme that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Started with &lt;code&gt;github.com&lt;/code&gt;, and if pasted to a browser won&amp;rsquo;t open to a file. Their appeal was that you can more easily write them by hand if you know the path within a GitHub repo.&lt;/li&gt;
&lt;li&gt;Started with &lt;code&gt;cdnjs.com&lt;/code&gt;, and if pasted to a browser will open a web page with all the versions of the library. The appeal here is that you will get the latest version.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both of them had problems though.&lt;/p&gt;
&lt;p&gt;The GitHub ones seemed to have never been used by users, likely because you need to guess what the path should look like, and you can always just go get a real URL to the raw file.&lt;/p&gt;
&lt;p&gt;While the cdnjs ones have some more usage, they are both a lot more complicated to support, as they require multiple requests to figure out what needs to be loaded. They also change over time. In addition the only known use at the moment is based on a very old example from an issue and it is even pointing to concrete, old version, of a library.&lt;/p&gt;
&lt;p&gt;Given that this can be done with a normal URL, we have decided to drop support for this and have warned users for the last couple of versions.&lt;/p&gt;
&lt;h3 id=&#34;deprecated-k6experimentaltracing-in-favor-of-a-javascript-implementation&#34;&gt;Deprecated &lt;code&gt;k6/experimental/tracing&lt;/code&gt; in favor of a JavaScript implementation&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;k6/experimental/tracing&lt;/code&gt; is arguably not very well named, and there is a good chance we would like to use the name for actual trace and span support within k6 in the future.&lt;/p&gt;
&lt;p&gt;On top of that it can now be fully supported in js code, which is why &lt;a href=&#34;/docs/k6/latest/javascript-api/jslib/http-instrumentation-tempo/&#34;&gt;http-instrumentation-tempo
&lt;/a&gt; was created.&lt;/p&gt;
&lt;p&gt;The JavaScript implementation is a drop-in replacement, so all you need to do is replace &lt;code&gt;k6/experimental/tracing&lt;/code&gt; with &lt;code&gt;https://jslib.k6.io/http-instrumentation-tempo/1.0.0/index.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The module is &lt;a href=&#34;https://github.com/grafana/k6/pull/3855&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;planned to be removed in v0.55.0&lt;/a&gt;, planned for November 11th, 2024.&lt;/p&gt;
&lt;h3 id=&#34;experimental-websockets-now-require-binarytype-to-be-set-to-receive-binary-messages&#34;&gt;Experimental websockets now require &lt;code&gt;binaryType&lt;/code&gt; to be set to receive binary messages&lt;/h3&gt;
&lt;p&gt;As part of the stabilization of the &lt;code&gt;k6/experimental/websockets&lt;/code&gt; we need to move the default value of &lt;code&gt;binaryType&lt;/code&gt; to &lt;code&gt;blob&lt;/code&gt;. It was previously &lt;code&gt;arraybuffer&lt;/code&gt; and since the last version there was a warning that it needs to be set in order for binary messages to be received.&lt;/p&gt;
&lt;p&gt;That warning is now an error.&lt;/p&gt;
&lt;p&gt;In the future we will move the default value to &lt;code&gt;blob&lt;/code&gt; and remove the error.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;p&gt;The new features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Native ESM support, which also brings some quality of life JavaScript features&lt;/li&gt;
&lt;li&gt;Blob support in the experimental websockets module&lt;/li&gt;
&lt;li&gt;Experimental OpenTelemetry metrics output&lt;/li&gt;
&lt;li&gt;Consolidating cloud related commands and features under &lt;code&gt;k6 cloud&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;native-esm-support-3456&#34;&gt;Native ESM support &lt;a href=&#34;https://github.com/grafana/k6/pull/3456&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3456&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this feature k6 is now ES6&#43; compliant natively. Which means (asterisk free) support for &lt;a href=&#34;https://github.com/grafana/k6/issues/824&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;the spread operator with object&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/issues/2887&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;private class fields&lt;/a&gt; and &lt;a href=&#34;https://github.com/grafana/k6/issues/2168&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;optional chaining&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But also faster startup times, more consistent errors and easier addition of features as we now only need to add them to Sobek instead of also them being supported in the internal Babel.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;History of compatibility mode and ECMAScript specification compliance&lt;/summary&gt;
&lt;p&gt;Some history: More than 6 years ago k6 started using core-js and babel to get ES6&#43; features. core-js is a implementation of a lot of the types and their features such as &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;String.prototype.matchAll&lt;/code&gt;&lt;/a&gt; among other things, and Babel gets one piece of code that uses some syntax and returns a piece of code doing the same thing (mostly) but with different syntax. Usually with the idea of supporting newer syntax but returning code that can run on runtimes which only support old syntax.&lt;/p&gt;
&lt;p&gt;This is great, but it means that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For core-js each VU needs to run a bunch of JS code each initialization so it can polyfill everything that is missing&lt;/li&gt;
&lt;li&gt;Babel needs to be parsed and loaded and then given files to transpile on each start.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both of those aren&amp;rsquo;t that big problems usually, but the runtime k6 uses is fairly fast, but isn&amp;rsquo;t V8. What it lacks in speed it gets back in being easy to interact with from Go, the language k6 is written in.&lt;/p&gt;
&lt;p&gt;But it means that now on each start it needs to do a bunch of work that adds up.&lt;/p&gt;
&lt;p&gt;So long time ago for people who would want to not have to do this we added &lt;a href=&#34;https://github.com/grafana/k6/pull/1206&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;compatibility-mode=base&lt;/a&gt;. This allowed you to potentially not use this features and get a big speedup. Or use them outside of k6 and likely still get significant speed up if you cut down on it.&lt;/p&gt;
&lt;p&gt;At the same time the author and maintainer of the JS runtime we used (goja) did implement a &lt;em&gt;big&lt;/em&gt; portion of what we were missing from core-js and also Babel. After some experiments to cut down the core-js we import we ended up contributing back the remaining parts and dropping the whole library. Which lead to 5 times reduction of memory per VU for simple scripts. And even for fairly complicated ones.&lt;/p&gt;
&lt;p&gt;With this in mind we did try to cut down Babel as well and contribute back the simpler things it was used for. This over the years lead to small pieces of what Babel did being moved to goja and then disabled in Babel. Some of those were just easy wins, some of those were things that had very bad pathological cases where using a particular syntax made transpilation times explode.&lt;/p&gt;
&lt;p&gt;In all of that work there always were small (or not so small) breaking changes due to many factors - sometimes our new implementation was slightly wrong and we needed to fix, sometimes more than what was in the standard was enabled in core-js or Babel, sometimes the standard changed on those. And sometimes the implementation in Babel or core-js wasn&amp;rsquo;t as full and didn&amp;rsquo;t account for all corner cases.&lt;/p&gt;
&lt;p&gt;ECMAScript Modules(ESM) is the last such feature that Babel was used for. It also happens to be likely the one &lt;em&gt;most&lt;/em&gt; people used, due to the fact that it is the standard way to reuse code and import libraries.&lt;/p&gt;
&lt;p&gt;While the work on this feature started over 2 years ago, it both depended on other features that weren&amp;rsquo;t there yet, but also interacts with more or less every other feature that is part of the ECMAScript standard.&lt;/p&gt;
&lt;p&gt;Along the way there were many internal refactors as well as additional tests to make certain we can be as backwards compatible as possible. But there also ended up being things that just weren&amp;rsquo;t going to be compatible, like the listed breaking changes.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;After ESM now being natively supported, compatibility-mode &lt;code&gt;base&lt;/code&gt; vs &lt;code&gt;extended&lt;/code&gt; has only 1 feature difference - aliasing &lt;code&gt;global&lt;/code&gt; to &lt;code&gt;globalThis&lt;/code&gt; to make it a bit more compatible with (old) Node.js. There is ongoing &lt;a href=&#34;https://github.com/grafana/k6/issues/3864&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;discussion&lt;/a&gt; if that as well should be removed.&lt;/p&gt;
&lt;p&gt;For the purposes of having less intrusive changes and shipping this earlier a few things have not been implemented in k6. That includes top-level-await and dynamic import support. Both of them are likely to land in the next version.&lt;/p&gt;
&lt;h3 id=&#34;importmetaresolve-gets-an-url-from-a-relative-path-the-same-way-import-or-require-does-3873&#34;&gt;&lt;code&gt;import.meta.resolve()&lt;/code&gt; gets an URL from a relative path the same way &lt;code&gt;import&lt;/code&gt; or &lt;code&gt;require&lt;/code&gt; does &lt;a href=&#34;https://github.com/grafana/k6/pull/3873&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3873&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As part of the move to ESM a lot of cases where k6 currently do not resolve the same relative path to the same file were found. Some of those were fixed - as those in &lt;code&gt;require&lt;/code&gt;, but others haven&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;It also became apparent some users do use the relativity of &lt;code&gt;require&lt;/code&gt;, but also &lt;code&gt;open&lt;/code&gt;. As we move to make this consistent among uses, we decided to let users have a better transition path forward.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;import.meta.resolve&lt;/code&gt; will give you just a new URL that can be used in all functions and it will give you the same result.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;import.meta.resolve&lt;/code&gt; uses the same algorithm and relativity as ESM import syntax. Refer to &lt;a href=&#34;/docs/k6/latest/javascript-api/import.meta/resolve/&#34;&gt;docs&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3 id=&#34;blob-support-in-the-experimental-websockets-module-grafanaxk6-websockets74&#34;&gt;Blob support in the experimental websockets module &lt;a href=&#34;https://github.com/grafana/xk6-websockets/pull/74&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;grafana/xk6-websockets#74&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to support the default &lt;code&gt;WebSocket.binaryType&lt;/code&gt; type as per spec (&lt;code&gt;&amp;quot;blob&amp;quot;&lt;/code&gt;), we have added support for the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Blob&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;Blob&lt;/code&gt; interface&lt;/a&gt; as part of the features included in the &lt;code&gt;xk6-websockets&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;So, from now on it can be used with &lt;code&gt;import { Blob } from &amp;quot;k6/experimental/websockets&amp;quot;;&lt;/code&gt;. In the future, apart from graduating this module to stable, we might also want to expose the &lt;code&gt;Blob&lt;/code&gt; interface globally (no imports will be required). But for now, please remind that its support is still experimental, as the entire module is. Refer to the &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/websockets/blob/&#34;&gt;docs&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3 id=&#34;experimental-opentelemetry-output-3834&#34;&gt;Experimental OpenTelemetry Output &lt;a href=&#34;https://github.com/grafana/k6/pull/3834&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3834&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release introduces a new experimental output for OpenTelemetry. This allows users to send k6 metrics to any OpenTelemetry-compatible backends. More details and usage examples can be found in the &lt;a href=&#34;/docs/k6/latest/results-output/real-time/opentelemetry/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To output metrics to OpenTelemetry, use the &lt;code&gt;experimental-opentelemetry&lt;/code&gt; output option:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt; k6 run -o experimental-opentelemetry examples/script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you have any feedback or issues, please let us know directly in &lt;a href=&#34;https://github.com/grafana/xk6-output-opentelemetry/issues&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;the extension repository&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;consolidating-cloud-features-under-k6-cloud-3813&#34;&gt;Consolidating cloud features under &lt;code&gt;k6 cloud&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/3813&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3813&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release introduces the first iteration of the revamped cloud-related commands under the &lt;code&gt;k6 cloud&lt;/code&gt; command, featuring two new subcommands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;k6 cloud login&lt;/code&gt;: replaces &lt;code&gt;k6 login cloud&lt;/code&gt; for authenticating with the cloud service. It supports token-based authentication only. The previous authentication method using email and password will still be available through the legacy &lt;code&gt;k6 login cloud&lt;/code&gt; command, which is now deprecated and will be removed in a future release (no removal date set yet).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;k6 cloud run&lt;/code&gt;: is the new official way to run k6 on the cloud service, serving as an alternative to the existing &lt;code&gt;k6 cloud&lt;/code&gt; command. The &lt;code&gt;k6 cloud&lt;/code&gt; command will remain available for a few more versions but will eventually function only as a wrapper for all cloud-related commands, without any direct functionality.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.53.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0530-release-notes">Version 0.53.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.53.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Native ECMAScript modules support&lt;/li>
&lt;li>New experimental OpenTelemetry metrics output&lt;/li>
&lt;li>Blob support in experimental websockets module&lt;/li>
&lt;li>Consolidate cloud features and commands under &lt;code>k6 cloud&lt;/code>&lt;/li>
&lt;li>Breaking change: remove magic URL resolutions&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;h3 id="require-is-now-specification-compliant-and-always-resolves-based-on-the-file-it-is-written-in-3534">Require is now specification compliant and always resolves based on the file it is written in &lt;a href="https://github.com/grafana/k6/issues/3534" target="_blank" rel="noopener noreferrer">#3534&lt;/a>&lt;/h3>
&lt;p>The &lt;code>require&lt;/code> function in k6 used to resolve identifiers based on the current &amp;ldquo;root of execution&amp;rdquo; (more on that later). In a lot of cases, that aligns with the file the &lt;code>require&lt;/code> is written in or a file in the same folder, which leads to the same result. In a small subset of cases, this isn&amp;rsquo;t the case.&lt;/p></description></item><item><title>Version 0.52.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.52.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.52.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0520-release-notes&#34;&gt;Version 0.52.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.52.0&lt;/code&gt; is here 🎉! Some special mentions included in this release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#switch-goja-to-our-own-fork-named-sobek-3775&#34;&gt;We&amp;rsquo;ve switched to our own fork of &lt;code&gt;goja&lt;/code&gt; named &lt;code&gt;sobek&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#panics-are-no-longer-being-captured-3777&#34;&gt;Panics are no longer captured&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#experimental-support-for-typescript-and-es6-using-esbuild-3738&#34;&gt;We&amp;rsquo;ve added experimental support for TypeScript and ES6&#43;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6/browser&lt;/code&gt; has &lt;a href=&#34;#k6browser-has-graduated-from-an-experimental-module-3793&#34;&gt;graduated from an experimental module&lt;/a&gt;, and now has a &lt;a href=&#34;#k6browser-has-now-a-fully-async-api-browser428&#34;&gt;fully Async API&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;h3 id=&#34;switch-goja-to-our-own-fork-named-sobek-3775&#34;&gt;Switch &lt;code&gt;goja&lt;/code&gt; to our own fork named &lt;code&gt;sobek&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/3775&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3775&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To accelerate the development speed and bring ECMAScript Modules (ESM) support to k6 earlier (&lt;a href=&#34;https://github.com/grafana/k6/issues/3265%29&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://github.com/grafana/k6/issues/3265)&lt;/a&gt;,
we have decided to create a fork of the &lt;a href=&#34;https://github.com/dop251/goja/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;goja&lt;/code&gt;&lt;/a&gt; project under the Grafana GitHub organization,
named &lt;a href=&#34;https://github.com/grafana/sobek&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;sobek&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Starting on this release, k6 (and its extensions) now use &lt;code&gt;sobek&lt;/code&gt; instead of the original &lt;code&gt;goja&lt;/code&gt;, for all (of the
publicly exposed parts of the API) except for a couple of packages that are only used internally by k6.&lt;/p&gt;
&lt;p&gt;All k6 extensions linked in the docs have had a PR for this transition opened, as explained in this &lt;a href=&#34;https://github.com/grafana/k6/issues/3773#issuecomment-2182113677&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;comment&lt;/a&gt;. Any extension author who hasn&amp;rsquo;t gotten a PR can follow the same steps.&lt;/p&gt;
&lt;p&gt;Find further details in &lt;a href=&#34;https://github.com/grafana/k6/issues/3772&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3772&lt;/a&gt; and &lt;a href=&#34;https://github.com/grafana/k6/issues/3773&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3773&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;panics-are-no-longer-being-captured-3777&#34;&gt;Panics are no longer being captured &lt;a href=&#34;https://github.com/grafana/k6/pull/3777&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3777&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since this release, Go panics are no longer being captured by k6. This means that if a panic occurs while running a test,
the k6 process will crash, and the panic stack trace will be printed to the console.&lt;/p&gt;
&lt;p&gt;We decided to change this behavior because it&amp;rsquo;s something that was left from the past as a safeguard, but it&amp;rsquo;s not as
good as it might seem. For most cases with multiple goroutines/async, it&amp;rsquo;s not enough and also makes a bunch of potential
bugs seem like less of an issue.&lt;/p&gt;
&lt;p&gt;Thus, this will help us to identify and fix bugs earlier, improve the overall stability of k6, and
most likely make the experience of developing k6 extensions friendlier.&lt;/p&gt;
&lt;h3 id=&#34;libstate-no-longer-has-group-3750&#34;&gt;&lt;code&gt;lib.State&lt;/code&gt; no longer has &lt;code&gt;Group&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/3750&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3750&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As the result of refactoring the implementation of &lt;code&gt;group&lt;/code&gt; and &lt;code&gt;check&lt;/code&gt; methods, in order to decouple them, and thus
enable other future improvements, the &lt;code&gt;lib.State&lt;/code&gt; object no longer has a &lt;code&gt;Group&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;This change should not affect most users, except for a couple of extensions, for which the use of &lt;code&gt;Group&lt;/code&gt; was
already questionable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;xk6-fasthttp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xk6-g0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;other-breaking-changes&#34;&gt;Other breaking changes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3797&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3797&lt;/a&gt; starts using &lt;code&gt;-&lt;/code&gt; as a special value for &lt;code&gt;--archive-out&lt;/code&gt; to output the archive to stdout.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1318&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1318&lt;/a&gt; makes the &lt;code&gt;Mouse.up&lt;/code&gt; and &lt;code&gt;Mouse.down&lt;/code&gt; methods no longer take x and y coordinates. Instead, they dispatch events on the current mouse position.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;experimental-support-for-typescript-and-es6-using-esbuild-3738&#34;&gt;Experimental support for TypeScript and ES6&#43; using esbuild &lt;a href=&#34;https://github.com/grafana/k6/pull/3738&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3738&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release of k6 introduces experimental support for TypeScript and ES6&#43; using esbuild, thanks to a new
&lt;a href=&#34;/docs/k6/latest/using-k6/javascript-typescript-compatibility-mode/&#34;&gt;compatibility mode&lt;/a&gt; named &lt;code&gt;experimental_enhanced&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;sh&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-sh&#34;&gt;k6 run --compatibility-mode=experimental_enhanced script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With this new compatibility mode, the test source code is transformed using esbuild instead of Babel, which also means
that source files with the extension &lt;em&gt;&amp;quot;.ts&amp;quot;&lt;/em&gt; are loaded by esbuild&amp;rsquo;s TypeScript loader, which results in partial
TypeScript support: it removes the type information but doesn&amp;rsquo;t provide type safety.&lt;/p&gt;
&lt;h3 id=&#34;k6browser-has-graduated-from-an-experimental-module-3793&#34;&gt;&lt;code&gt;k6/browser&lt;/code&gt; has graduated from an experimental module &lt;a href=&#34;https://github.com/grafana/k6/pull/3793&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3793&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module is now available as &lt;code&gt;k6/browser&lt;/code&gt; instead of &lt;code&gt;k6/experimental/browser&lt;/code&gt;. The previous &lt;code&gt;k6/experimental/browser&lt;/code&gt; module will be removed on September 23rd, 2024. Refer to &lt;a href=&#34;/docs/k6/latest/using-k6-browser/migrating-to-k6-v0-52/&#34;&gt;the migration guide&lt;/a&gt; for more information on how to update your scripts.&lt;/p&gt;
&lt;h3 id=&#34;k6browser-has-now-a-fully-async-api-browser428&#34;&gt;&lt;code&gt;k6/browser&lt;/code&gt; has now a fully Async API &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/428&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#428&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release introduces a fully Async API for the &lt;code&gt;k6/browser&lt;/code&gt; module. This means that nearly all the methods in the module now return promises. This change is part of the ongoing effort to make the browser module more user-friendly and easier to use. Please see &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-browser/&#34;&gt;the browser documentation&lt;/a&gt; for more information on how to use the new Async API.&lt;/p&gt;
&lt;p&gt;Related Changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1310&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1310&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1311&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1311&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1312&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1312&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1316&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1316&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1328&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1328&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1337&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1337&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1367&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1367&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1366&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1366&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1314&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1314&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1332&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1332&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1323&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1323&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1355&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1355&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1348&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1348&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1364&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1364&lt;/a&gt; Migrates &lt;code&gt;Browser&lt;/code&gt;, &lt;code&gt;BrowserContext&lt;/code&gt;, &lt;code&gt;ElementHandle&lt;/code&gt;, &lt;code&gt;Frame&lt;/code&gt;, &lt;code&gt;JSHandle&lt;/code&gt;, &lt;code&gt;Keyboard&lt;/code&gt;, &lt;code&gt;Locator&lt;/code&gt;, &lt;code&gt;Mouse&lt;/code&gt;, &lt;code&gt;Page&lt;/code&gt;, &lt;code&gt;Request&lt;/code&gt;, &lt;code&gt;Response&lt;/code&gt; APIs to async.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.52.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0520-release-notes">Version 0.52.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.52.0&lt;/code> is here 🎉! Some special mentions included in this release:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="#switch-goja-to-our-own-fork-named-sobek-3775">We&amp;rsquo;ve switched to our own fork of &lt;code>goja&lt;/code> named &lt;code>sobek&lt;/code>&lt;/a>.&lt;/li>
&lt;li>&lt;a href="#panics-are-no-longer-being-captured-3777">Panics are no longer captured&lt;/a>.&lt;/li>
&lt;li>&lt;a href="#experimental-support-for-typescript-and-es6-using-esbuild-3738">We&amp;rsquo;ve added experimental support for TypeScript and ES6+&lt;/a>.&lt;/li>
&lt;li>&lt;code>k6/browser&lt;/code> has &lt;a href="#k6browser-has-graduated-from-an-experimental-module-3793">graduated from an experimental module&lt;/a>, and now has a &lt;a href="#k6browser-has-now-a-fully-async-api-browser428">fully Async API&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;h3 id="switch-goja-to-our-own-fork-named-sobek-3775">Switch &lt;code>goja&lt;/code> to our own fork named &lt;code>sobek&lt;/code> &lt;a href="https://github.com/grafana/k6/pull/3775" target="_blank" rel="noopener noreferrer">#3775&lt;/a>&lt;/h3>
&lt;p>To accelerate the development speed and bring ECMAScript Modules (ESM) support to k6 earlier (&lt;a href="https://github.com/grafana/k6/issues/3265%29" target="_blank" rel="noopener noreferrer">https://github.com/grafana/k6/issues/3265)&lt;/a>,
we have decided to create a fork of the &lt;a href="https://github.com/dop251/goja/" target="_blank" rel="noopener noreferrer">&lt;code>goja&lt;/code>&lt;/a> project under the Grafana GitHub organization,
named &lt;a href="https://github.com/grafana/sobek" target="_blank" rel="noopener noreferrer">&lt;code>sobek&lt;/code>&lt;/a>.&lt;/p></description></item><item><title>Version 0.51.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.51.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.51.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0510-release-notes&#34;&gt;Version 0.51.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.51.0&lt;/code&gt; is here 🎉! Some special mentions included in this release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#introduction-of-k6experimentalstreams-module-3696&#34;&gt;A new experimental streams module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#new-features-and-updates-of-webcrypto-api-support-3714&#34;&gt;New algorithms for WebCrypto module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#timers-globally-available-3589&#34;&gt;Timers are globally available&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;h3 id=&#34;transition-browser-apis-to-async&#34;&gt;Transition browser APIs to Async&lt;/h3&gt;
&lt;p&gt;In the last release notes &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.50.0.md#browser-apis-to-async&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;we mentioned&lt;/a&gt; this breaking change, and we wanted to remind and update you on the plan. In the &lt;strong&gt;next&lt;/strong&gt; release (v0.52.0), most of the synchronous browser APIs will be migrated to be asynchronous (promisifying them). We expect this will affect most if not all of our users.&lt;/p&gt;
&lt;p&gt;This breaking change will require you to add &lt;code&gt;await&lt;/code&gt; in front of most of the browser module APIs. Without this &lt;code&gt;await&lt;/code&gt; you will witness undocumented and unknown behavior during the runtime. To make the migration simpler we advise that you work with the latest &lt;a href=&#34;/docs/k6/latest/set-up/configure-k6-intellisense/&#34;&gt;k6 type definitions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can find a list of all the APIs that we expect to convert to async in a comment in issue &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/428#issuecomment-1964020837&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#428&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Awaiting on something that’s not a thenable just returns that value, which means you can add the &lt;code&gt;await&lt;/code&gt; keyword today on the APIs that will become async to future proof your test scripts.&lt;/p&gt;
&lt;p&gt;Here are the reasons for making this large breaking change:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Most browser APIs use some form of long-running IO operation (networking) to perform the requested action on the web browser against the website under test. We need to avoid blocking JavaScript&amp;rsquo;s runtime event loop for such operations.&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re going to add more asynchronous event-based APIs (such as &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/1227&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;page.on&lt;/a&gt;) that our current synchronous APIs would block.&lt;/li&gt;
&lt;li&gt;To align with how developers expect to work with JavaScript APIs.&lt;/li&gt;
&lt;li&gt;To have better compatibility with Playwright.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a starting point, we have migrated a single API (the &lt;code&gt;tap&lt;/code&gt; method), which you can find the details below that will help visualize the upcoming breaking changes.&lt;/p&gt;
&lt;h3 id=&#34;browser-tap-is-now-an-async-method-grafanaxk6-browser1268&#34;&gt;Browser &lt;code&gt;Tap&lt;/code&gt; is now an async method grafana/xk6-browser#1268&lt;/h3&gt;
&lt;p&gt;This release converts the &lt;code&gt;Tap&lt;/code&gt; method in the &lt;code&gt;browser&lt;/code&gt; module into an asynchronous method. This change is necessary to ensure that the method can be used in async contexts and to align with the rest of the browser module&amp;rsquo;s planned asynchronous API. To use the &lt;code&gt;Tap&lt;/code&gt; method, you must now add the &lt;code&gt;await&lt;/code&gt; keyword before the method call.&lt;/p&gt;
&lt;p&gt;Affected components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/browser/locator/tap/&#34;&gt;&lt;code&gt;locator.tap&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/browser/page/tap/&#34;&gt;&lt;code&gt;page.tap&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/browser/frame/&#34;&gt;&lt;code&gt;frame.tap&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/browser/elementhandle/&#34;&gt;&lt;code&gt;elementHandle.tap&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See the following example for how to use the &lt;code&gt;Tap&lt;/code&gt; method after this change:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import browser from &amp;#39;k6/experimental/browser&amp;#39;;

// ...

export default function () {
  // ...
  page.tap(selector, { modifiers: [&amp;#39;Alt&amp;#39;, &amp;#39;Control&amp;#39;, &amp;#39;Meta&amp;#39;, &amp;#39;Shift&amp;#39;] });
  // ...
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import browser from &amp;#39;k6/experimental/browser&amp;#39;

// ...

export default function () {
	// ...
	await page.tap(selector, { modifiers: [&amp;#34;Alt&amp;#34;, &amp;#34;Control&amp;#34;, &amp;#34;Meta&amp;#34;, &amp;#34;Shift&amp;#34;] });
	// ...
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;k6experimentalwebsockets-will-not-default-binarytype-to-arraybuffer&#34;&gt;&lt;code&gt;k6/experimental/websockets&lt;/code&gt; will not default &lt;code&gt;binaryType&lt;/code&gt; to `&amp;ldquo;arraybuffer&amp;rdquo;&#39;&lt;/h3&gt;
&lt;p&gt;As part of the stabilization of the API it needs to become as close to the specification.&lt;/p&gt;
&lt;p&gt;Early in the development the idea of adding &lt;code&gt;Blob&lt;/code&gt; support as part was deemed feature creep and was dropped in favor of going with only &lt;code&gt;&amp;quot;arraybuffer&amp;quot;&lt;/code&gt;. But the specification defaults to returning binary responses as &lt;code&gt;Blob&lt;/code&gt; - which was another thing that was changed.&lt;/p&gt;
&lt;p&gt;While adding &lt;code&gt;Blob&lt;/code&gt; is still on our radar, moving the default is always going to be a breaking change that we need to do to align with the specification.&lt;/p&gt;
&lt;p&gt;For this release there is now a warning that will be printed if &lt;code&gt;binaryType&lt;/code&gt; is not set to &lt;code&gt;&amp;quot;arraybuffer&amp;quot;&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; a binary response is received. The warning will go away when &lt;code&gt;binaryType&lt;/code&gt; is set to &lt;code&gt;&amp;quot;arraybuffer&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the next release the warning will become an error.&lt;/p&gt;
&lt;p&gt;More info and place for discussion can be found in an &lt;a href=&#34;https://github.com/grafana/xk6-websockets/issues/67&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;this issue&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;k6experimentalgrpc-is-no-longer-available-3530&#34;&gt;&lt;code&gt;k6/experimental/grpc&lt;/code&gt; is no longer available &lt;a href=&#34;https://github.com/grafana/k6/pull/3530&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3530&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As the last step of the graduation process for the experimental gRPC module, we completely removed the module. It is now fully integrated into the stable &lt;code&gt;k6/net/grpc&lt;/code&gt; module. So, if you haven&amp;rsquo;t done this yet, replace your imports from &lt;code&gt;k6/experimental/grpc&lt;/code&gt; to &lt;code&gt;k6/net/grpc&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;deprecations&#34;&gt;Deprecations&lt;/h3&gt;
&lt;p&gt;The following pull requests start the process to introduce breaking changes. They are currently starting to emit warning if their condition is hit, but they will turn to return errors in the future release.
It is recommended to use the suggested alternative, or to fix the script if you see the warning message.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3681&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3681&lt;/a&gt; Use of not-compliant &lt;code&gt;require&lt;/code&gt; expressions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3680&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3680&lt;/a&gt; Modules resolution of modules not previously seen during the initialization phase.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3676&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3676&lt;/a&gt; Working directory is set to the current location when the script is provided using stdin, instead of the root folder.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3671&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3530&lt;/a&gt; Automagically resolve modules from cdnjs and github &amp;ldquo;URLs&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;introduction-of-k6experimentalstreams-module-3696&#34;&gt;Introduction of &lt;code&gt;k6/experimental/streams&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/k6/pull/3696&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3696&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release of k6 introduces the new &lt;code&gt;k6/experimental/streams&lt;/code&gt; module, which partially supports the JavaScript
Streams API, focusing initially on the &lt;code&gt;ReadableStream&lt;/code&gt; construct.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;ReadableStream&lt;/code&gt;, users can define and consume data streams within k6 scripts. This is particularly useful for
efficiently handling large datasets or for processing data sequentially in a controlled flow.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of stream&#39;s usage&lt;/summary&gt;
&lt;p&gt;The following example demonstrates creating and consuming a simple stream that emits numbers until it reaches a predefined limit:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { ReadableStream } from &amp;#39;k6/experimental/streams&amp;#39;;

function numbersStream() {
  let currentNumber = 0;

  return new ReadableStream({
    start(controller) {
      const fn = () =&amp;gt; {
        if (currentNumber &amp;lt; 5) {
          controller.enqueue(&amp;#43;&amp;#43;currentNumber);
          setTimeout(fn, 1000);
          return;
        }

        controller.close();
      };
      setTimeout(fn, 1000);
    },
  });
}

export default async function () {
  const stream = numbersStream();
  const reader = stream.getReader();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    console.log(`received number ${value} from stream`);
  }

  console.log(&amp;#39;we are done&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;For more advanced examples, please head to the MDN Web Docs on the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Streams_API&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Streams API&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;limitations&#34;&gt;Limitations&lt;/h4&gt;
&lt;p&gt;Currently, users can define and consume readable streams. However, this release does not include support for byte readers
and controllers, nor does it include support the &lt;code&gt;tee&lt;/code&gt;, &lt;code&gt;pipeTo&lt;/code&gt;, and
&lt;code&gt;pipeThrough&lt;/code&gt; methods of the &lt;code&gt;ReadableStream&lt;/code&gt; object.&lt;/p&gt;
&lt;h3 id=&#34;new-features-and-updates-of-webcrypto-api-support-3714&#34;&gt;New features and updates of WebCrypto API support &lt;a href=&#34;https://github.com/grafana/k6/pull/3714&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3714&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This release brings support for asymmetric cryptography to the &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt; module. We added support of the elliptic curves algorithms ECDH (&lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/67&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-webcrypto#67&lt;/a&gt;) and ECDSA (&lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/69&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-webcrypto#69&lt;/a&gt;) algorithms along with new import/export key formats like &lt;code&gt;spki&lt;/code&gt; and &lt;code&gt;pkcs8&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;One of the newly added operations is &lt;code&gt;deriveBits&lt;/code&gt;, which allows parties to generate a unique shared secret by using shared public and non-shared private keys.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of generating a shared secret for Alice and Bob.&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { crypto } from &amp;#39;k6/experimental/webcrypto&amp;#39;;

export default async function () {
  // Generate a key pair for Alice
  const aliceKeyPair = await crypto.subtle.generateKey(
    {
      name: &amp;#39;ECDH&amp;#39;,
      namedCurve: &amp;#39;P-256&amp;#39;,
    },
    true,
    [&amp;#39;deriveKey&amp;#39;, &amp;#39;deriveBits&amp;#39;]
  );

  // Generate a key pair for Bob
  const bobKeyPair = await crypto.subtle.generateKey(
    {
      name: &amp;#39;ECDH&amp;#39;,
      namedCurve: &amp;#39;P-256&amp;#39;,
    },
    true,
    [&amp;#39;deriveKey&amp;#39;, &amp;#39;deriveBits&amp;#39;]
  );

  // Derive shared secret for Alice
  const aliceSharedSecret = await deriveSharedSecret(aliceKeyPair.privateKey, bobKeyPair.publicKey);

  // Derive shared secret for Bob
  const bobSharedSecret = await deriveSharedSecret(bobKeyPair.privateKey, aliceKeyPair.publicKey);

  // alice shared secret and bob shared secret should be the same
  console.log(&amp;#39;alice shared secret: &amp;#39; &amp;#43; printArrayBuffer(aliceSharedSecret));
  console.log(&amp;#39;bob shared secret: &amp;#39; &amp;#43; printArrayBuffer(bobSharedSecret));
}

async function deriveSharedSecret(privateKey, publicKey) {
  return crypto.subtle.deriveBits(
    {
      name: &amp;#39;ECDH&amp;#39;,
      public: publicKey,
    },
    privateKey,
    256
  );
}

const printArrayBuffer = (buffer) =&amp;gt; {
  const view = new Uint8Array(buffer);
  return Array.from(view);
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;The &lt;code&gt;sign&lt;/code&gt; and &lt;code&gt;verify&lt;/code&gt; operations got support for ECDSA algorithm. The &lt;code&gt;sign&lt;/code&gt; operation allows you to sign a message with a private key, while the &lt;code&gt;verify&lt;/code&gt; operation allows you to verify the signature with a public key.&lt;/p&gt;
&lt;p&gt;Other notable updates and fixes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/68&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-webcrypto#68&lt;/a&gt; fixes a degradation for the sign/verify operations for HMAC algorithm.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/75&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-webcrypto#75&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/76&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-webcrypto#76&lt;/a&gt; refactor webcrypto module to be thread-safe.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/74&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-webcrypto#74&lt;/a&gt; adds JWK import/export support for ECDH and ECDSA. Refactors JWK import/export to use only go standard library.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/webcrypto/&#34;&gt;webcrypto&amp;rsquo;s module documentation&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3 id=&#34;timers-globally-available-3589&#34;&gt;Timers globally available &lt;a href=&#34;https://github.com/grafana/k6/pull/3589&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3589&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;setInterval&lt;/code&gt; and related clear functions have been part of the JavaScript ecosystem, probably for as long as it has existed.&lt;/p&gt;
&lt;p&gt;In the previous releases we stabilized and made them available through &lt;code&gt;k6/timers&lt;/code&gt; module. While the module isn&amp;rsquo;t going anywhere and might get more identifiers, &lt;code&gt;setTimeout&lt;/code&gt; is usually used without importing it. For this reason it is now globally available along &lt;code&gt;clearTimeout&lt;/code&gt;, &lt;code&gt;setInterval&lt;/code&gt; and &lt;code&gt;clearInterval&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;No code needs to be changed, but you no longer need to import &lt;code&gt;k6/timers&lt;/code&gt; to use this functionality.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.51.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0510-release-notes">Version 0.51.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.51.0&lt;/code> is here 🎉! Some special mentions included in this release:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="#introduction-of-k6experimentalstreams-module-3696">A new experimental streams module&lt;/a>&lt;/li>
&lt;li>&lt;a href="#new-features-and-updates-of-webcrypto-api-support-3714">New algorithms for WebCrypto module&lt;/a>&lt;/li>
&lt;li>&lt;a href="#timers-globally-available-3589">Timers are globally available&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;h3 id="transition-browser-apis-to-async">Transition browser APIs to Async&lt;/h3>
&lt;p>In the last release notes &lt;a href="https://github.com/grafana/k6/blob/master/release%20notes/v0.50.0.md#browser-apis-to-async" target="_blank" rel="noopener noreferrer">we mentioned&lt;/a> this breaking change, and we wanted to remind and update you on the plan. In the &lt;strong>next&lt;/strong> release (v0.52.0), most of the synchronous browser APIs will be migrated to be asynchronous (promisifying them). We expect this will affect most if not all of our users.&lt;/p></description></item><item><title>Version 0.50.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.50.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.50.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0500-release-notes&#34;&gt;Version 0.50.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.50.0&lt;/code&gt; is here 🎉!&lt;/p&gt;
&lt;p&gt;This release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adds support for uploading files from the browser module.&lt;/li&gt;
&lt;li&gt;Introduces the &lt;code&gt;options.cloud&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Stabilizes the previously experimental timers module as the &lt;code&gt;k6/timers&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;Brings JSON Web Key support to the &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt; module.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-websockets/pull/60&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;websockets#60&lt;/a&gt; allows manually setting the &lt;code&gt;name&lt;/code&gt; tag, which also overwrites the &lt;code&gt;url&lt;/code&gt; tag with the &lt;code&gt;name&lt;/code&gt; value. This change makes it consistent with the logic that was implemented in k6 v0.41. Thanks, @mkadirtan for contributing!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;browser-apis-to-async&#34;&gt;Browser APIs to Async&lt;/h3&gt;
&lt;p&gt;In future releases, we are going to be moving most of the synchronous browser APIs to asynchronous ones (promisifying them). We expect this will affect most of our users, so we are posting this upfront before making the change. Here are the reasons for making this large breaking change:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Most browser APIs use some form of long-running IO operation (networking) to perform the requested action on the web browser against the website under test. We need to avoid blocking JavaScript&amp;rsquo;s runtime event loop for such operations.&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re going to add more asynchronous event-based APIs (such as &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/1227&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;page.on&lt;/a&gt;) that our current synchronous APIs would block.&lt;/li&gt;
&lt;li&gt;To align with how developers expect to work with JavaScript APIs.&lt;/li&gt;
&lt;li&gt;To have better compatibility with Playwright.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can find a list of all the APIs that we expect to convert to async in a comment in issue &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/428#issuecomment-1964020837&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#428&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Awaiting on something that’s not a thenable just returns that value, which means you can add the &lt;code&gt;await&lt;/code&gt; keyword against APIs that will become async to future proof your test scripts.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;add-support-for-uploading-files-from-the-browser-module-browser1097-browser1244&#34;&gt;Add support for uploading files from the browser module &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1097&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1097&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1244&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1244&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can now upload files using the available input forms on the website under test. The new API is &lt;code&gt;setInputFiles&lt;/code&gt; which can be called from a &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;frame&lt;/code&gt; or &lt;code&gt;elementHandle&lt;/code&gt; types. It can upload one or more files encoded in the test script. To upload files from the local file system, work with the &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/fs/&#34;&gt;experimental fs module&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Expand to see the examples.&lt;/summary&gt;
&lt;p&gt;For the following examples, we will use the HTML file:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;HTML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form method=&amp;#34;POST&amp;#34; action=&amp;#34;/upload&amp;#34; enctype=&amp;#34;multipart/form-data&amp;#34;&amp;gt;
      &amp;lt;input type=&amp;#34;file&amp;#34; name=&amp;#34;upl&amp;#34; id=&amp;#34;upload&amp;#34; multiple /&amp;gt;
      &amp;lt;input type=&amp;#34;submit&amp;#34; value=&amp;#34;Send&amp;#34; /&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Uploading a file can be achieved with the following script:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;// Import the k6 encoder module.
import encoding from &amp;#39;k6/encoding&amp;#39;;
...
export default async function () {
  const page = browser.newPage();

  await page.goto(url)

  // Encode and upload some data into a plain text file called test.txt.
  page.setInputFiles(&amp;#39;input[id=&amp;#34;upload&amp;#34;]&amp;#39;, { name: &amp;#39;test.txt&amp;#39;, mimetype: &amp;#39;text/plain&amp;#39;, buffer: encoding.b64encode(&amp;#39;Hello World&amp;#39;) })

  // Click on the submit button on the form to upload the file.
  const submitButton = page.locator(&amp;#39;input[type=&amp;#34;submit&amp;#34;]&amp;#39;)
  await Promise.all([page.waitForNavigation(), submitButton.click()])

  page.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Uploading multiple files can be done with the use of an array:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;page.setInputFiles(&amp;#39;input[id=&amp;#34;upload&amp;#34;]&amp;#39;, [
  { name: &amp;#39;test.txt&amp;#39;, mimetype: &amp;#39;text/plain&amp;#39;, buffer: encoding.b64encode(&amp;#39;Hello World&amp;#39;) },
  {
    name: &amp;#39;test.json&amp;#39;,
    mimetype: &amp;#39;text/json&amp;#39;,
    buffer: encoding.b64encode(&amp;#39;{&amp;#34;message&amp;#34;: &amp;#34;Hello World&amp;#34;}&amp;#39;),
  },
]);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;Thanks to @bandorko! :bow: :tada:&lt;/p&gt;
&lt;h3 id=&#34;introducing-optionscloud-3348-3407&#34;&gt;Introducing options.cloud &lt;a href=&#34;https://github.com/grafana/k6/pull/3348&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3348&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/3407&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3407&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this release, we introduce a new way of defining cloud options. From now on, you can use &lt;code&gt;options.cloud&lt;/code&gt; instead of &lt;code&gt;options.ext.loadimpact&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To migrate, you can move the &lt;code&gt;loadimpact&lt;/code&gt; object to the root of the &lt;code&gt;options&lt;/code&gt; object and rename it to &lt;code&gt;cloud&lt;/code&gt;. For example:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;export let options = {
  ext: {
    loadimpact: {
      name: &amp;#39;Legacy way of defining cloud options&amp;#39;,
      projectID: 12345,
    },
  },
};

export let options = {
  cloud: {
    name: &amp;#39;Current way of defining cloud options&amp;#39;,
    projectID: 12345,
  },
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;All scripts with legacy &lt;code&gt;options.ext.loadimpact&lt;/code&gt; will continue to function as before. There&amp;rsquo;s no planned sunset date for the legacy option, but we highly encourage using &lt;code&gt;options.cloud&lt;/code&gt; going forward. For more details about cloud options, refer to &lt;a href=&#34;/docs/grafana-cloud/k6/author-run/cloud-scripting-extras/cloud-options/&#34;&gt;Cloud options&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;timers-api-becomes-part-of-the-k6-core-3587&#34;&gt;Timers API becomes part of the k6 core &lt;a href=&#34;https://github.com/grafana/k6/pull/3587&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3587&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this release, the timers API is no longer experimental and can be imported as &lt;code&gt;k6/timers&lt;/code&gt; instead of as &lt;code&gt;k6/experimental/timers&lt;/code&gt;. The later will be supported until &lt;code&gt;v0.52.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also contribute to the discussion on making the current timer exports globally available in &lt;a href=&#34;https://github.com/grafana/k6/issues/3589&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3589&lt;/a&gt;, or just give it a :&#43;1:.&lt;/p&gt;
&lt;h3 id=&#34;json-web-key-support-in-k6experimentalwebcrypto-module-webcrypto61&#34;&gt;JSON Web Key support in &lt;code&gt;k6/experimental/webcrypto&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/xk6-webcrypto/pull/61&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;webcrypto#61&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The experimental webcrypto module now supports the JSON Web Key (JWK) format, using the &lt;code&gt;importKey&lt;/code&gt; and &lt;code&gt;exportKey&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;This allows you to import and export keys in the JWK format for the supported algorithms.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;const generatedKey = await crypto.subtle.generateKey({ name: &amp;#39;AES-CBC&amp;#39;, length: &amp;#39;256&amp;#39; }, true, [
  &amp;#39;encrypt&amp;#39;,
  &amp;#39;decrypt&amp;#39;,
]);

const exportedKey = await crypto.subtle.exportKey(&amp;#39;jwk&amp;#39;, generatedKey);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;ux-improvements-and-enhancements&#34;&gt;UX improvements and enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1197&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1197&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1202&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1202&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1203&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1203&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1221&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1221&lt;/a&gt; adds the ability to upload screenshots to a remote location.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1209&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1209&lt;/a&gt; adds a shadow DOM usage example.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1233&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1233&lt;/a&gt; returns actionable errors for &lt;code&gt;evaluate&lt;/code&gt; APIs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1228&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1228&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1232&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1232&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1235&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1235&lt;/a&gt; injects the &lt;code&gt;testRunId&lt;/code&gt; into the &lt;code&gt;window.k6&lt;/code&gt; object for external applications to query (for example, Grafana Faro).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;browser-context-isolation-browser1112&#34;&gt;Browser Context Isolation &lt;a href=&#34;https://github.com/grafana/xk6-browser/issues/1112&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1112&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this release, we have overhauled and (tremendously) improved the performance and stability of the browser module. It&amp;rsquo;s now possible to run tests with a larger number of VUs concurrently without any performance issues. Previously, when running tests with multiple VUs concurrently, each VU&amp;rsquo;s browser context would attach to the pages from the other VUs&amp;rsquo; browser contexts. This led to unexpected behavior and performance issues and, to an extent, reduced the module&amp;rsquo;s capability to run multi-VU tests.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.50.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0500-release-notes">Version 0.50.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.50.0&lt;/code> is here 🎉!&lt;/p>
&lt;p>This release:&lt;/p>
&lt;ul>
&lt;li>Adds support for uploading files from the browser module.&lt;/li>
&lt;li>Introduces the &lt;code>options.cloud&lt;/code> option.&lt;/li>
&lt;li>Stabilizes the previously experimental timers module as the &lt;code>k6/timers&lt;/code> module.&lt;/li>
&lt;li>Brings JSON Web Key support to the &lt;code>k6/experimental/webcrypto&lt;/code> module.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/xk6-websockets/pull/60" target="_blank" rel="noopener noreferrer">websockets#60&lt;/a> allows manually setting the &lt;code>name&lt;/code> tag, which also overwrites the &lt;code>url&lt;/code> tag with the &lt;code>name&lt;/code> value. This change makes it consistent with the logic that was implemented in k6 v0.41. Thanks, @mkadirtan for contributing!&lt;/li>
&lt;/ul>
&lt;h3 id="browser-apis-to-async">Browser APIs to Async&lt;/h3>
&lt;p>In future releases, we are going to be moving most of the synchronous browser APIs to asynchronous ones (promisifying them). We expect this will affect most of our users, so we are posting this upfront before making the change. Here are the reasons for making this large breaking change:&lt;/p></description></item><item><title>Version 0.49.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.49.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.49.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0490-release-notes&#34;&gt;Version 0.49.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 &lt;code&gt;v0.49.0&lt;/code&gt; is here 🎉! This release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adds a built-in &lt;a href=&#34;/docs/k6/latest/results-output/web-dashboard/&#34;&gt;web dashboard&lt;/a&gt; that displays test results in real time.&lt;/li&gt;
&lt;li&gt;Introduces &lt;code&gt;clear&lt;/code&gt; functionality to the browser module&amp;rsquo;s &lt;code&gt;locator&lt;/code&gt; classes.&lt;/li&gt;
&lt;li&gt;Merges the gRPC experimental module back into the gRPC core module.&lt;/li&gt;
&lt;li&gt;Enables the ability to get the selection from an element in &lt;code&gt;k6/html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Collects internal modules and outputs used by a script.&lt;/li&gt;
&lt;li&gt;Prepares &lt;code&gt;k6/experimental/timers&lt;/code&gt; for stabilization.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3494&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3494&lt;/a&gt; stops updating &lt;code&gt;loadimpact/k6&lt;/code&gt; docker image. If you still use it, please migrate to the &lt;a href=&#34;https://hub.docker.com/r/grafana/k6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;grafana/k6&lt;/a&gt; image.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1111&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1111&lt;/a&gt; removes &lt;code&gt;timeout&lt;/code&gt; option for &lt;code&gt;isVisible&lt;/code&gt; and &lt;code&gt;isHidden&lt;/code&gt; since the API no longer waits for the element to appear on the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;web-dashboard&#34;&gt;Web Dashboard&lt;/h3&gt;
&lt;p&gt;The new &lt;a href=&#34;/docs/k6/latest/results-output/web-dashboard/&#34;&gt;web dashboard&lt;/a&gt; brings real-time visualization to load testing. This feature allows users to monitor test progress and analyze
results dynamically, enhancing the overall testing experience.&lt;/p&gt;
&lt;h4 id=&#34;real-time-test-results&#34;&gt;Real-time test results&lt;/h4&gt;
&lt;p&gt;Activate this feature using the environment variable &lt;code&gt;K6_WEB_DASHBOARD=true&lt;/code&gt;. For this initial release, the dashboard is not enabled by default to allow users to opt into this new experience as it evolves.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;K6_WEB_DASHBOARD=true k6 run script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once enabled and the test script is running, navigate to &lt;a href=&#34;http://localhost:5665&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http://localhost:5665&lt;/a&gt; in your web browser to access the dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;https://github.com/grafana/xk6-dashboard/blob/master/screenshot/k6-dashboard-overview-light.png?raw=true&#34;
  alt=&#34;k6 Web Dashboard Overview&#34;/&gt;&lt;/p&gt;
&lt;h4 id=&#34;test-report&#34;&gt;Test report&lt;/h4&gt;
&lt;p&gt;The web dashboard also offers an HTML test report (see &lt;a href=&#34;https://github.com/grafana/xk6-dashboard/blob/master/screenshot/k6-dashboard-html-report-screen-view.png?raw=true&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;an example&lt;/a&gt;) for detailed analysis, enabling easy sharing and downloading capabilities for
collaboration.&lt;/p&gt;
&lt;p&gt;To access and download the report, click on the &lt;strong&gt;Report&lt;/strong&gt; button in the dashboard&amp;rsquo;s top right corner or use the &lt;code&gt;K6_WEB_DASHBOARD_EXPORT&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;K6_WEB_DASHBOARD=true K6_WEB_DASHBOARD_EXPORT=test-report.html k6 run script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-clear-to-the-locator-class-browser1149&#34;&gt;Add &lt;code&gt;clear&lt;/code&gt; to the &lt;code&gt;locator&lt;/code&gt; class &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1149&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1149&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The new &lt;code&gt;clear&lt;/code&gt; method on the &lt;code&gt;locator&lt;/code&gt; 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.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of the new functionality.&lt;/summary&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { check } from &amp;#39;k6&amp;#39;;
import { browser } from &amp;#39;k6/experimental/browser&amp;#39;;

export const options = {
  scenarios: {
    ui: {
      executor: &amp;#39;shared-iterations&amp;#39;,
      options: {
        browser: {
          type: &amp;#39;chromium&amp;#39;,
        },
      },
    },
  },
};

export default async function () {
  const context = browser.newContext();
  const page = context.newPage();

  await page.goto(&amp;#39;https://test.k6.io/my_messages.php&amp;#39;, { waitUntil: &amp;#39;networkidle&amp;#39; });

  // To mimic an input field with existing text.
  page.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;).type(&amp;#39;admin&amp;#39;);

  check(page, {
    not_empty: (p) =&amp;gt; p.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;).inputValue() != &amp;#39;&amp;#39;,
  });

  // Clear the text.
  page.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;).clear();

  check(page, {
    empty: (p) =&amp;gt; p.locator(&amp;#39;input[name=&amp;#34;login&amp;#34;]&amp;#39;).inputValue() == &amp;#39;&amp;#39;,
  });

  page.close();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&#34;add-tracing-to-the-browser-module-browser1100&#34;&gt;Add tracing to the browser module &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1100&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1100&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now generates traces that provide a representation of its inner workings, such as API methods executed (for example &lt;code&gt;browser.newPage&lt;/code&gt; and &lt;code&gt;page.goto&lt;/code&gt;), page navigations, and &lt;a href=&#34;/docs/k6/latest/using-k6-browser/metrics/#googles-core-web-vitals&#34;&gt;Web Vitals&lt;/a&gt; measurements.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The traces generation for the browser module depends on the overall &lt;code&gt;k6&lt;/code&gt; traces option introduced in &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.48.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.48.0&lt;/a&gt;. Check out the &lt;a href=&#34;/docs/k6/latest/using-k6/k6-options/reference/#traces-output&#34;&gt;documentation&lt;/a&gt; to learn more about it.&lt;/p&gt;
&lt;h3 id=&#34;grpc-streaming-api-becomes-part-of-the-k6-core-3490&#34;&gt;gRPC streaming API becomes part of the k6 core &lt;a href=&#34;https://github.com/grafana/k6/pull/3490&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3490&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this release, gRPC&amp;rsquo;s streaming API becomes part of the core&amp;rsquo;s &lt;code&gt;k6/net/grpc&lt;/code&gt; module. The experimental &lt;code&gt;k6/experimental/grpc&lt;/code&gt; has been back-merged into the core.&lt;/p&gt;
&lt;p&gt;You can still use import &lt;code&gt;k6/experimental/grpc&lt;/code&gt; for a couple of releases, but it&amp;rsquo;s deprecated and will be removed in the future (planned in k6 version &lt;code&gt;v0.51.0&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;To migrate your scripts, replace &lt;code&gt;k6/experimental/grpc&lt;/code&gt; with &lt;code&gt;k6/net/grpc&lt;/code&gt; in your script imports, and the code should work as before.&lt;/p&gt;
&lt;h3 id=&#34;k6html-extract-selection-from-element-3519&#34;&gt;k6/html: Extract selection from element &lt;a href=&#34;https://github.com/grafana/k6/pull/3519&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3519&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/docs/k6/latest/javascript-api/k6-html/&#34;&gt;&lt;code&gt;k6/html&lt;/code&gt;&lt;/a&gt; has been around for a while and allows you to search within an HTML document with a jQuery-like API called &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-html/selection/&#34;&gt;Selection&lt;/a&gt;, and also has support for the more standard &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-html/element/&#34;&gt;Element&lt;/a&gt; that represents DOM element.&lt;/p&gt;
&lt;p&gt;For a long time, you could get an Element from a Selection using the &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-html/selection/selection-get/&#34;&gt;&lt;code&gt;.get(index)&lt;/code&gt;&lt;/a&gt;, but you couldn&amp;rsquo;t get back to a Selection from an Element.&lt;/p&gt;
&lt;p&gt;This is not a common case, but one that requires quite a bit of code. For example, see the following jQuery snippet:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;let li = http.get(&amp;#39;https://test.k6.io&amp;#39;).html().find(&amp;#39;li&amp;#39;);
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(&amp;#39;ul.header-icons&amp;#39;);
  // 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&amp;#39;t know which one it should
  // work against
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In order to support the above example, you can use &lt;code&gt;selection&lt;/code&gt;, without going to the element:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;let li = http.get(&amp;#39;https://test.k6.io&amp;#39;).html().find(&amp;#39;li&amp;#39;);
for (; li.size() &amp;gt; 0; li = li.next()) {
  let ul = li.closest(&amp;#39;ul.header-icons&amp;#39;); // li here is still a selection and we iterate over it.
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is not always possible though, and arguably isn&amp;rsquo;t what most users will naturally do.&lt;/p&gt;
&lt;p&gt;Because of this, we have now added a new &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-html/element/element-selection/&#34;&gt;&lt;code&gt;.selection()&lt;/code&gt;&lt;/a&gt; which returns a selection for its element.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;let li = http.get(&amp;#39;https://test.k6.io&amp;#39;).html().find(&amp;#39;li&amp;#39;);
li.each(function (_, element) {
  let container = element.selection().closest(&amp;#39;ul.header-icons&amp;#39;);
  // .. more code
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thanks to @Azhovan! :bow: :tada:&lt;/p&gt;
&lt;h3 id=&#34;collect-usage-data-on-imported-internal-modules-and-outputs-3525&#34;&gt;Collect usage data on imported internal modules and outputs &lt;a href=&#34;https://github.com/grafana/k6/pull/3525&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3525&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 now collects usage data of the modules and outputs that are being used when the &lt;a href=&#34;/docs/k6/latest/misc/usage-collection/&#34;&gt;usage report&lt;/a&gt; is enabled. The data collection is only related to the built-in k6 modules and outputs. Private, custom modules and extensions &lt;a href=&#34;https://github.com/grafana/k6/blob/f35e67902605877ebf2c5e9c8673cd7faf4cdc1e/cmd/report.go#L33-L57&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;are never collected&lt;/a&gt;. The usage report is enabled by default in k6, but it is possible to opt-out using the &lt;a href=&#34;/docs/k6/latest/using-k6/k6-options/reference/#no-usage-report&#34;&gt;no-usage-report&lt;/a&gt; option.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.49.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0490-release-notes">Version 0.49.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 &lt;code>v0.49.0&lt;/code> is here 🎉! This release:&lt;/p>
&lt;ul>
&lt;li>Adds a built-in &lt;a href="/docs/k6/latest/results-output/web-dashboard/">web dashboard&lt;/a> that displays test results in real time.&lt;/li>
&lt;li>Introduces &lt;code>clear&lt;/code> functionality to the browser module&amp;rsquo;s &lt;code>locator&lt;/code> classes.&lt;/li>
&lt;li>Merges the gRPC experimental module back into the gRPC core module.&lt;/li>
&lt;li>Enables the ability to get the selection from an element in &lt;code>k6/html&lt;/code>.&lt;/li>
&lt;li>Collects internal modules and outputs used by a script.&lt;/li>
&lt;li>Prepares &lt;code>k6/experimental/timers&lt;/code> for stabilization.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3494" target="_blank" rel="noopener noreferrer">#3494&lt;/a> stops updating &lt;code>loadimpact/k6&lt;/code> docker image. If you still use it, please migrate to the &lt;a href="https://hub.docker.com/r/grafana/k6" target="_blank" rel="noopener noreferrer">grafana/k6&lt;/a> image.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/xk6-browser/pull/1111" target="_blank" rel="noopener noreferrer">browser#1111&lt;/a> removes &lt;code>timeout&lt;/code> option for &lt;code>isVisible&lt;/code> and &lt;code>isHidden&lt;/code> since the API no longer waits for the element to appear on the page.&lt;/li>
&lt;/ul>
&lt;h2 id="new-features">New features&lt;/h2>
&lt;h3 id="web-dashboard">Web Dashboard&lt;/h3>
&lt;p>The new &lt;a href="/docs/k6/latest/results-output/web-dashboard/">web dashboard&lt;/a> brings real-time visualization to load testing. This feature allows users to monitor test progress and analyze
results dynamically, enhancing the overall testing experience.&lt;/p></description></item><item><title>Version 0.48.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.48.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.48.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0480-release-notes&#34;&gt;Version 0.48.0 release notes&lt;/h1&gt;
&lt;!-- md-k6:skipall --&gt;
&lt;p&gt;k6 v0.48.0 is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Numerous long-awaited breaking changes.&lt;/li&gt;
&lt;li&gt;A new &lt;code&gt;k6 new&lt;/code&gt; subcommand to generate a new test script.&lt;/li&gt;
&lt;li&gt;A new &lt;code&gt;k6/experimental/fs&lt;/code&gt; module for file interactions.&lt;/li&gt;
&lt;li&gt;CPU and network throttling support for the k6 browser module.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3448&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3448&lt;/a&gt; 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 &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.45.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.45&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3439&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3439&lt;/a&gt; changes the &lt;code&gt;Client&lt;/code&gt; signature in &lt;code&gt;k6/experimental/redis&lt;/code&gt; module. Refer to the module-related section below.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3350&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3350&lt;/a&gt; removes the &lt;code&gt;grpc.invoke()&lt;/code&gt;&amp;rsquo;s parameter &lt;code&gt;headers&lt;/code&gt;, deprecated in k6 &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.37.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.37&lt;/a&gt;. Use the &lt;code&gt;metadata&lt;/code&gt; parameter instead.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3389&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3389&lt;/a&gt; removes the &lt;code&gt;--logformat&lt;/code&gt; flag, deprecated in &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.38.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.38&lt;/a&gt;. Use the &lt;code&gt;--log-format&lt;/code&gt; flag instead.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3390&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3390&lt;/a&gt; removes all CSV output&amp;rsquo;s CLI arguments, deprecated in &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.35.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.35&lt;/a&gt;. This change makes the CSV output consistent with other output formats.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3365&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3365&lt;/a&gt; removes the &lt;code&gt;k6 convert&lt;/code&gt; CLI command, deprecated in &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.41.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.41&lt;/a&gt;. Use the &lt;a href=&#34;https://github.com/grafana/har-to-k6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;har-to-k6&lt;/a&gt; package instead.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3451&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3451&lt;/a&gt; removes logic that would attempt to prepend a &lt;code&gt;https://&lt;/code&gt; scheme to module specifiers that were not recognized. Deprecated in k6 &lt;a href=&#34;https://github.com/grafana/k6/releases/tag/v0.25.0&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;v0.25&lt;/a&gt;. Use full URLs if you want to load remote modules instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;add-k6-new-subcommand-3394&#34;&gt;Add &lt;code&gt;k6 new&lt;/code&gt; subcommand &lt;a href=&#34;https://github.com/grafana/k6/pull/3394&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3394&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;k6&lt;/code&gt; now has a &lt;code&gt;new&lt;/code&gt; 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:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;k6 new [filename]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If no filename is provided, k6 uses &lt;code&gt;script.js&lt;/code&gt; 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 &lt;code&gt;k6 run&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;add-a-k6experimentalfs-module-3165&#34;&gt;Add a &lt;code&gt;k6/experimental/fs&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/k6/pull/3165&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3165&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;k6&lt;/code&gt; now has a new &lt;code&gt;k6/experimenal/fs&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;Unlike the traditional &lt;a href=&#34;/docs/k6/latest/javascript-api/init-context/open/&#34;&gt;open&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;For more information, refer to the &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/fs/&#34;&gt;module documentation&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of the new functionality.&lt;/summary&gt;
&lt;p&gt;This example shows the new module usage:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import fs from &amp;#39;k6/experimental/fs&amp;#39;;

// k6 doesn&amp;#39;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&amp;#39;t impact other VUs.
let file;
(async function () {
  file = await open(&amp;#39;bonjour.txt&amp;#39;);
})();

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

  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 &amp;#43;= bytesRead;

    // If bytesRead is less than the buffer size, we&amp;#39;ve read the whole file
    if (bytesRead &amp;lt; buffer.byteLength) {
      break;
    }
  }

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

  // Seek back to the beginning of the file
  await file.seek(0, SeekMode.Start);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&#34;redis-mtls-support-and-new-client-constructor-options-3439-xk6-redis17&#34;&gt;Redis (m)TLS support and new Client constructor options &lt;a href=&#34;https://github.com/grafana/k6/pull/3439&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3439&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-redis/pull/17&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-redis/#17&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this release, the &lt;code&gt;k6/experimental/redis&lt;/code&gt; module receives several important updates, including breaking changes.&lt;/p&gt;
&lt;h4 id=&#34;connection-urls&#34;&gt;Connection URLs&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Client&lt;/code&gt; constructor now supports connection URLs to configure connections to Redis servers or clusters. These URLs can be in the format &lt;code&gt;redis://[[username][:password]@][host][:port][/db-number]&lt;/code&gt; for standard connections, or &lt;code&gt;rediss://[[username][]:password@]][host][:port][/db-number]&lt;/code&gt; for TLS-secured connections. For more details, refer to the &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/redis/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h5 id=&#34;example-usage&#34;&gt;Example usage&lt;/h5&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import redis from &amp;#39;k6/experimental/redis&amp;#39;;

const redisClient = new redis.Client(&amp;#39;redis://someusername:somepassword@localhost:6379/0&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 id=&#34;revamped-options-object&#34;&gt;Revamped Options object&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Client&lt;/code&gt; constructor has been updated with a new &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/redis/redis-options/&#34;&gt;Options&lt;/a&gt; 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 &lt;a href=&#34;/docs/k6/latest/javascript-api/k6-experimental/redis/redis-options/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of the new functionality.&lt;/summary&gt;
&lt;p&gt;This example shows the usage of the new &lt;code&gt;Options&lt;/code&gt; object:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import redis from &amp;#39;k6/experimental/redis&amp;#39;;

const redisClient = new redis.Client({
  socket: {
    host: &amp;#39;localhost&amp;#39;,
    port: 6379,
  },
  username: &amp;#39;someusername&amp;#39;,
  password: &amp;#39;somepassword&amp;#39;,
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h4 id=&#34;mtls-support&#34;&gt;(m)TLS support&lt;/h4&gt;
&lt;p&gt;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&amp;rsquo;s &lt;a href=&#34;/docs/k6/latest/using-k6/k6-options/reference/#insecure-skip-tls-verify&#34;&gt;insecureSkipTLSVerify&lt;/a&gt; option (set to &lt;code&gt;true&lt;/code&gt;).&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt; Expand to see an example of the new functionality.&lt;/summary&gt;
&lt;p&gt;This example shows the configuration of a TLS connection:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import redis from &amp;#39;k6/experimental/redis&amp;#39;;

const redisClient = new redis.Client({
  socket: {
    host: &amp;#39;localhost&amp;#39;,
    port: 6379,
    tls: {
      ca: [open(&amp;#39;ca.crt&amp;#39;)],
      cert: open(&amp;#39;client.crt&amp;#39;), // client certificate
      key: open(&amp;#39;client.key&amp;#39;), // client private key
    },
  },
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&#34;add-tracing-instrumentation-3445&#34;&gt;Add tracing instrumentation &lt;a href=&#34;https://github.com/grafana/k6/pull/3445&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3445&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;k6&lt;/code&gt; now supports a new &lt;em&gt;traces output&lt;/em&gt; option that allows you to configure the output for traces generated during its execution. This option can be set through the &lt;code&gt;--traces-output&lt;/code&gt; argument in the &lt;code&gt;k6 run&lt;/code&gt; command or by setting the &lt;code&gt;K6_TRACES_OUTPUT&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;Currently, no traces are generated by &lt;code&gt;k6&lt;/code&gt; itself, but this feature represents the first step towards richer tracing functionalities in &lt;code&gt;k6&lt;/code&gt; and its extensions.&lt;/p&gt;
&lt;p&gt;By default traces output is set to &lt;code&gt;none&lt;/code&gt;, and currently the only supported output is &lt;code&gt;otel&lt;/code&gt; which uses the &lt;a href=&#34;https://github.com/open-telemetry/opentelemetry-go&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;opentelemetry-go&lt;/a&gt;&amp;rsquo;s &lt;a href=&#34;https://opentelemetry.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Open Telemetry&lt;/a&gt; API and SDK implementations. The format for the &lt;code&gt;otel&lt;/code&gt; &lt;em&gt;traces output&lt;/em&gt; configuration is the following:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;--traces-output=&amp;lt;endpoint&amp;gt;[,opt1=val1,opt2=val2]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Where &lt;code&gt;opt&lt;/code&gt;s can be one of the following options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;proto&lt;/code&gt;: Specifies the protocol to use in the connection to the traces backend. Supports &lt;code&gt;grpc&lt;/code&gt; &lt;em&gt;(default)&lt;/em&gt; and &lt;code&gt;http&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;header.&amp;lt;header_name&amp;gt;&lt;/code&gt;: Specifies an additional header to include in the connection to the traces backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;K6_TRACES_OUTPUT=https://traces.k6.io/v1/traces,proto=http,header.Authorization=Bearer token&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-support-for-browser-modules-pagethrottlecpu-browser1095&#34;&gt;Add support for browser module&amp;rsquo;s &lt;code&gt;page.throttleCPU&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1095&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1095&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports throttling the CPU from chrome/chromium&amp;rsquo;s perspective by using the &lt;code&gt;throttleCPU&lt;/code&gt; API, which helps emulate slower devices when testing the website&amp;rsquo;s frontend. It requires an argument of type &lt;code&gt;CPUProfile&lt;/code&gt;, which includes a &lt;code&gt;rate&lt;/code&gt; field that is a slow-down factor, where &lt;code&gt;1&lt;/code&gt; means no throttling, &lt;code&gt;2&lt;/code&gt; means 2x slowdown, and so on. For more details, refer to the &lt;a href=&#34;/docs/k6/v0.48.x/javascript-api/k6-experimental/browser/page/throttlecpu/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;...
  const context = browser.newContext();
  const page = context.newPage();

  try {
    page.throttleCPU({ rate: 4 });
...&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-support-for-browser-modules-pagethrottlenetwork-browser1094&#34;&gt;Add support for browser module&amp;rsquo;s &lt;code&gt;page.throttleNetwork&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1094&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1094&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser module now supports throttling the characteristics of the network from chrome/chromium&amp;rsquo;s perspective by using the &lt;code&gt;throttleNetwork&lt;/code&gt; API, which helps emulate slow network connections when testing the website&amp;rsquo;s frontend. It requires an argument of type &lt;code&gt;NetworkProfile&lt;/code&gt;, with a definition of:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;ts&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-ts&#34;&gt;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;
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can either define your own network profiles or use the ones we have defined by importing &lt;code&gt;networkProfiles&lt;/code&gt; from the &lt;code&gt;browser&lt;/code&gt; module. For more details, refer to the &lt;a href=&#34;/docs/k6/v0.48.x/javascript-api/k6-experimental/browser/page/throttlenetwork/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import { browser, networkProfiles } from &amp;#39;k6/experimental/browser&amp;#39;;
...
  const context = browser.newContext();
  const page = context.newPage();

  try {
    page.throttleNetwork(networkProfiles[&amp;#39;Slow 3G&amp;#39;]);
...&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;k6s-documentation-is-moving-under-grafanacomdocsk6&#34;&gt;k6&amp;rsquo;s documentation is moving under &lt;a href=&#34;/docs/k6/&#34;&gt;grafana.com/docs/k6&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s not directly part of the k6 v0.48 release, but we believe it is worth mentioning that we&amp;rsquo;re moving the documentation from &lt;a href=&#34;https://k6.io/docs/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;k6.io/docs&lt;/a&gt; to &lt;a href=&#34;/docs/k6/&#34;&gt;grafana.com/docs/k6&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The legacy documentation space &lt;code&gt;k6.io/docs&lt;/code&gt; will be available for a while, but we encourage you to update your bookmarks and links to the new domain.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.48.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0480-release-notes">Version 0.48.0 release notes&lt;/h1>
&lt;!-- md-k6:skipall -->
&lt;p>k6 v0.48.0 is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>Numerous long-awaited breaking changes.&lt;/li>
&lt;li>A new &lt;code>k6 new&lt;/code> subcommand to generate a new test script.&lt;/li>
&lt;li>A new &lt;code>k6/experimental/fs&lt;/code> module for file interactions.&lt;/li>
&lt;li>CPU and network throttling support for the k6 browser module.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>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.&lt;/p></description></item><item><title>Version 0.47.0 release notes</title><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.47.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v0.47.0/</guid><content><![CDATA[&lt;h1 id=&#34;version-0470-release-notes&#34;&gt;Version 0.47.0 release notes&lt;/h1&gt;
&lt;p&gt;k6 &lt;code&gt;v0.47.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;h2 id=&#34;deprecations&#34;&gt;Deprecations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3347&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3347&lt;/a&gt; The built-in &lt;code&gt;statsd&lt;/code&gt; output option has been deprecated, and users should use the &lt;a href=&#34;https://github.com/LeonAdato/xk6-output-statsd&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-output-statsd&lt;/a&gt; extension instead. See &lt;a href=&#34;https://github.com/grafana/k6/issues/2982&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#2982&lt;/a&gt; for future plans.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/3288&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3288&lt;/a&gt; Loading remote modules now requires users to prepend them with &lt;code&gt;https://&lt;/code&gt;. Before, k6 would try to resolve importing remote modules by prepending &lt;code&gt;https://&lt;/code&gt; if it was missing. This behavior has been deprecated and will be fully removed in the next release (v0.48.0).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;add-grpcs-binary-metadata-support-3234-xk6-grpc46&#34;&gt;Add gRPC&amp;rsquo;s binary metadata support &lt;a href=&#34;https://github.com/grafana/k6/pull/3234&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3234&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-grpc/pull/46&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-grpc#46&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The k6 gRPC modules (&lt;code&gt;k6/net/grpc&lt;/code&gt; and &lt;code&gt;k6/experimental/grpc&lt;/code&gt;) now support handling binary metadata that uses the &lt;code&gt;-bin&lt;/code&gt; postfix, according to the gRPC specification.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;let resp = client.invoke(
  &amp;#39;grpc.testing.TestService/EmptyCall&amp;#39;,
  {},
  { metadata: { &amp;#39;X-Load-Tester-bin&amp;#39;: new Uint8Array([2, 200]) } }
);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thanks to @sapphire-janrain for the contribution!&lt;/p&gt;
&lt;h3 id=&#34;add-grpcs-reflection-metadata-support-3343-xk6-grpc46&#34;&gt;Add gRPC&amp;rsquo;s reflection metadata support &lt;a href=&#34;https://github.com/grafana/k6/pull/3343&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3343&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-grpc/pull/46&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-grpc#46&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The k6 gRPC modules (&lt;code&gt;k6/net/grpc&lt;/code&gt; and &lt;code&gt;k6/experimental/grpc&lt;/code&gt;) now support adding metadata to reflection requests by using a new connection parameter &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-net-grpc/client/client-connect/#connectparams&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;reflectMetadata&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;higher-precision-for-trend-metrics-in-grafana-cloud-k6-3302&#34;&gt;Higher precision for Trend metrics in Grafana Cloud k6 &lt;a href=&#34;https://github.com/grafana/k6/pull/3302&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3302&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Grafana Cloud k6 is now able to store and visualize Trend metrics up to 3 digits of precision for decimal numbers.&lt;/p&gt;
&lt;h3 id=&#34;docker-support-for-browser-based-tests-3199&#34;&gt;Docker support for browser-based tests &lt;a href=&#34;https://github.com/grafana/k6/pull/3199&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3199&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 is now publishig Docker images that include Chromium web browser. This allows k6 users to run tests that use &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Browser API&lt;/a&gt; without having to install Chrome first. Check the &lt;a href=&#34;https://hub.docker.com/r/grafana/k6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&amp;ldquo;A note on running browser tests&amp;rdquo; section&lt;/a&gt; of the Overview page on DockerHub for details.&lt;/p&gt;
&lt;h3 id=&#34;docker-images-for-arm64-architecture-3320&#34;&gt;Docker images for ARM64 architecture &lt;a href=&#34;https://github.com/grafana/k6/pull/3320&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#3320&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The k6&amp;rsquo;s release process now builds and pushes dedicated Docker images for ARM64. Check k6&amp;rsquo;s &lt;a href=&#34;https://hub.docker.com/r/grafana/k6/tags&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;tags page&lt;/a&gt; on DockerHub for details.&lt;/p&gt;
&lt;h3 id=&#34;new-authentication-methods-and-http-headers-api-for-prometheus-remote-write-output-xk6-output-prometheus-remote143-xk6-output-prometheus-remote145-xk6-output-prometheus-remote147&#34;&gt;New authentication methods and HTTP headers API for Prometheus remote write output &lt;a href=&#34;https://github.com/grafana/xk6-output-prometheus-remote/pull/143&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-output-prometheus-remote#143&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-output-prometheus-remote/pull/145&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-output-prometheus-remote#145&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-output-prometheus-remote/pull/147&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-output-prometheus-remote#147&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The experimental Prometheus remote write output now supports two new authentication methods: Bearer token and TLS certificates. Check out the &lt;a href=&#34;https://k6.io/docs/results-output/real-time/prometheus-remote-write/#options&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;documentation&lt;/a&gt; to learn more about how to define them using the new environment variables.
We&amp;rsquo;ve also added the &lt;code&gt;K6_PROMETHEUS_RW_HTTP_HEADERS&lt;/code&gt; that defines a new and more convenient way to set custom HTTP headers to pass through each flush metrics&amp;rsquo; request.&lt;/p&gt;
&lt;h3 id=&#34;improved-the-browser-modules-cookie-api&#34;&gt;Improved the browser module&amp;rsquo;s cookie API&lt;/h3&gt;
&lt;p&gt;The browser module now provides a more complete and robust API for handling cookies. The cookie API was stabilized by defining a new &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/browsercontext/cookie&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;Cookie&lt;/code&gt; class&lt;/a&gt; (&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1008&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1008&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1030&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1030&lt;/a&gt;) that can be used while creating and retrieving cookies. This enabled us to add a new &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/browsercontext/cookies/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;browserContext.cookies([urls])&lt;/code&gt;&lt;/a&gt; method (&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1005&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1005&lt;/a&gt;) that returns all cookies from the current &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/browsercontext&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser context&lt;/a&gt;. The new API also supports filtering cookies by URL (&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1016&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1016&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;That led to fixing a bug where the &lt;code&gt;expires&lt;/code&gt; field was not being set correctly while adding cookies using the &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/browsercontext/addcookies/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;context.addCookie()&lt;/code&gt;&lt;/a&gt; method (&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1031&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1031&lt;/a&gt;). Lastly, the existing &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/browsercontext/clearcookies&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;context.clearCookies()&lt;/code&gt;&lt;/a&gt; method was fixed to clear all cookies from the current &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/browsercontext&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser context&lt;/a&gt; (&lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1040&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1040&lt;/a&gt;).&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;const context = browser.newContext();
context.addCookies([
  { name: &amp;#39;foo&amp;#39;, value: &amp;#39;bar&amp;#39;, url: &amp;#39;https://test.k6.io&amp;#39; },
  { name: &amp;#39;baz&amp;#39;, value: &amp;#39;qux&amp;#39;, url: &amp;#39;https://grafana.com&amp;#39; },
]);

const cookies = context.cookies(&amp;#39;https://test.k6.io&amp;#39;);
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&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;add-support-for-browser-modules-pageonconsole-browser1006&#34;&gt;Add support for browser module&amp;rsquo;s &lt;code&gt;page.on(&#39;console&#39;)&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/xk6-browser/pull/1006&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;browser#1006&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Allows users to register a handler to be executed every time the &lt;code&gt;console&lt;/code&gt; API methods are called from within the page&amp;rsquo;s JavaScript context. The arguments passed into the handler are defined by the &lt;a href=&#34;https://k6.io/docs/javascript-api/k6-experimental/browser/consolemessage/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ConsoleMessage&lt;/a&gt; class.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;page.on(&amp;#39;console&amp;#39;, (msg) =&amp;gt; {
  check(msg, {
    assertConsoleMessageType: (msg) =&amp;gt; msg.type() == &amp;#39;log&amp;#39;,
    assertConsoleMessageText: (msg) =&amp;gt; msg.text() == &amp;#39;this is a console.log message 42&amp;#39;,
    assertConsoleMessageArgs0: (msg) =&amp;gt;
      msg.args()[0].jsonValue() == &amp;#39;this is a console.log message&amp;#39;,
    assertConsoleMessageArgs1: (msg) =&amp;gt; msg.args()[1].jsonValue() == 42,
  });
});

page.evaluate(() =&amp;gt; console.log(&amp;#39;this is a console.log message&amp;#39;, 42));&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr /&gt;
&lt;p&gt;For a full list of changes, including UX improvements and bug fixes, refer to &lt;a href=&#34;https://github.com/grafana/k6/blob/master/release%20notes/v0.47.0.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;full release notes&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="version-0470-release-notes">Version 0.47.0 release notes&lt;/h1>
&lt;p>k6 &lt;code>v0.47.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;h2 id="deprecations">Deprecations&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3347" target="_blank" rel="noopener noreferrer">#3347&lt;/a> The built-in &lt;code>statsd&lt;/code> output option has been deprecated, and users should use the &lt;a href="https://github.com/LeonAdato/xk6-output-statsd" target="_blank" rel="noopener noreferrer">xk6-output-statsd&lt;/a> extension instead. See &lt;a href="https://github.com/grafana/k6/issues/2982" target="_blank" rel="noopener noreferrer">#2982&lt;/a> for future plans.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/3288" target="_blank" rel="noopener noreferrer">#3288&lt;/a> Loading remote modules now requires users to prepend them with &lt;code>https://&lt;/code>. Before, k6 would try to resolve importing remote modules by prepending &lt;code>https://&lt;/code> if it was missing. This behavior has been deprecated and will be fully removed in the next release (v0.48.0).&lt;/li>
&lt;/ul>
&lt;h2 id="new-features">New features&lt;/h2>
&lt;h3 id="add-grpcs-binary-metadata-support-3234-xk6-grpc46">Add gRPC&amp;rsquo;s binary metadata support &lt;a href="https://github.com/grafana/k6/pull/3234" target="_blank" rel="noopener noreferrer">#3234&lt;/a>, &lt;a href="https://github.com/grafana/xk6-grpc/pull/46" target="_blank" rel="noopener noreferrer">xk6-grpc#46&lt;/a>&lt;/h3>
&lt;p>The k6 gRPC modules (&lt;code>k6/net/grpc&lt;/code> and &lt;code>k6/experimental/grpc&lt;/code>) now support handling binary metadata that uses the &lt;code>-bin&lt;/code> postfix, according to the gRPC specification.&lt;/p></description></item><item><title/><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.8.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.8.0/</guid><content><![CDATA[&lt;p&gt;k6 &lt;code&gt;v1.8.0&lt;/code&gt; is here! This maintenance release on v1.x includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud secrets are now automatically available in &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; — use &lt;code&gt;--no-cloud-secrets&lt;/code&gt; to opt out.&lt;/li&gt;
&lt;li&gt;Pre-manifest extension dependencies are captured in archive metadata for cloud consumers.&lt;/li&gt;
&lt;li&gt;Structured logging from k6provider for better visibility into automatic extension provisioning.&lt;/li&gt;
&lt;li&gt;Multiple bug fixes for Browser, WebSocket, and Cloud modules.&lt;/li&gt;
&lt;li&gt;Dependency updates and reliability improvements.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;There are no breaking changes in this release.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;cloud-secrets-now-automatically-available-in-local-execution-5738--5875&#34;&gt;Cloud secrets now automatically available in local execution &lt;a href=&#34;https://github.com/grafana/k6/pull/5738&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5738&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5875&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5875&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When running &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;, secrets stored in Grafana Cloud are now automatically available to your script via &lt;code&gt;secrets.get()&lt;/code&gt; without any additional configuration.&lt;/p&gt;
&lt;p&gt;To opt out, pass &lt;code&gt;--no-cloud-secrets&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud run --local-execution --no-cloud-secrets script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Thanks, @vortegatorres!&lt;/p&gt;
&lt;h3 id=&#34;pre-manifest-extension-dependencies-captured-in-archive-metadata-5819--5977&#34;&gt;Pre-manifest extension dependencies captured in archive metadata &lt;a href=&#34;https://github.com/grafana/k6/pull/5819&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5819&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5977&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5977&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When creating a k6 archive (&lt;code&gt;k6 archive&lt;/code&gt;), extension dependency information is now captured in &lt;code&gt;metadata.json&lt;/code&gt; under a &lt;code&gt;&amp;quot;dependencies&amp;quot;&lt;/code&gt; field, using the constraints declared by the script before any external manifest overrides are applied. This ensures that &lt;code&gt;k6/x/&lt;/code&gt; imports are preserved correctly during auto-extension-resolution re-execution.&lt;/p&gt;
&lt;h2 id=&#34;ux-improvements-and-enhancements&#34;&gt;UX improvements and enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5815&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5815&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5993&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5993&lt;/a&gt; Warns when &lt;code&gt;http.get()&lt;/code&gt; or &lt;code&gt;http.head()&lt;/code&gt; receive extra arguments that are silently ignored, helping catch common scripting mistakes. Thanks, @moko-poi!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5657&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5657&lt;/a&gt; Adds automatic retries to &lt;code&gt;newAction&lt;/code&gt;-based Locator APIs in the browser module, improving reliability of browser tests. Thanks, @janHildebrandt98!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5845&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5845&lt;/a&gt; Wires k6provider&amp;rsquo;s structured logging into k6&amp;rsquo;s logger. Provisioning operations (artifact resolution, cache hits, downloads, retries, and cache pruning) now appear in k6&amp;rsquo;s log output at the correct level.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5855&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5855&lt;/a&gt; Fixes auto-extension-resolution incorrectly triggering binary provisioning when a script manifest specifies a dependency as a Go module pseudo-version (&lt;code&gt;v0.0.0&#43;sha&lt;/code&gt; or &lt;code&gt;v0.0.0-timestamp-sha&lt;/code&gt;), even when the running binary already satisfied the constraint.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5574&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5574&lt;/a&gt; Fixes position parser for base pointer options. Thanks, @chrismooreproductions!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5630&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5630&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5987&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5987&lt;/a&gt; Fixes duplicate redirect request metric emissions in the browser module, where each redirect was incorrectly emitting metrics for all prior redirects in the chain.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5620&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5620&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5991&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5991&lt;/a&gt; Preserves context cancellation causes across the scheduler, browser, and secret-source layers, improving the accuracy of error messages surfaced when a test is interrupted. Thanks, @LBaronceli!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5716&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5716&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5990&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5990&lt;/a&gt; Fixes WebSocket &lt;code&gt;bufferedAmount&lt;/code&gt; not being incremented when sending TypedArrays, causing it to go negative. Thanks, @prakharbirla-ng!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5814&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5814&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5975&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5975&lt;/a&gt; Fixes &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; ignoring &lt;code&gt;K6_CLOUD_PUSH_REF_ID&lt;/code&gt; and unconditionally creating a new test run instead of reusing the provided run ID. Thanks, @Reranko05!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5786&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5786&lt;/a&gt; Fixes swapped Min and Max values for Gauge metrics in Cloud output v2, which caused incorrect peak and floor values in cloud test result queries. Thanks, @esquonk!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5785&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5785&lt;/a&gt; Fixes a race condition in the browser module&amp;rsquo;s &lt;code&gt;handleExitEvent&lt;/code&gt; where &lt;code&gt;Done()&lt;/code&gt; was signalled before the subscription was removed, causing tests to hang until the timeout.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5905&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5905&lt;/a&gt; Fixes a deadlock where WebSocket connections hang forever during teardown when the server sends pings. A server ping arriving during shutdown could permanently stall the k6 process.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5923&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5923&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5989&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5989&lt;/a&gt; Fixes a nil pointer panic in &lt;code&gt;ElementHandle.DefaultTimeout&lt;/code&gt; (and any method that calls it, such as &lt;code&gt;GetAttribute&lt;/code&gt;) when invoked on a nil or partially-initialized handle. Thanks, @SAY-5!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-internal-improvements&#34;&gt;Maintenance and internal improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5896&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5896&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://github.com/grafana/k6/pull/5897&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5897&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel/exporters/otlp/otlptracehttp&lt;/code&gt; and &lt;code&gt;otlpmetrichttp&lt;/code&gt; to &lt;code&gt;v1.43.0&lt;/code&gt; [security], adding a 4 MiB response body cap to mitigate memory exhaustion from misconfigured or malicious servers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5857&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5857&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5950&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5950&lt;/a&gt; Updates Go to &lt;code&gt;v1.25.10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5946&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5946&lt;/a&gt; Updates &lt;code&gt;golang.org/x/net&lt;/code&gt; to &lt;code&gt;v0.55.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6042&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6042&lt;/a&gt; Updates Go toolchain to &lt;code&gt;v1.25.11&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6047&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6047&lt;/a&gt; Updates &lt;code&gt;golang.org/x/crypto&lt;/code&gt; to &lt;code&gt;v0.52.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5863&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5863&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5880&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5880&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5908&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5908&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/k6provider&lt;/code&gt; to &lt;code&gt;v0.5.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5740&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5740&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5796&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5796&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5910&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5910&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/sobek&lt;/code&gt; digest.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6035&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6035&lt;/a&gt; Move Docker Hub publishing to GAR mirror.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6064&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6064&lt;/a&gt; Bumps Dockerfile images for security updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;external-contributors&#34;&gt;External contributors&lt;/h2&gt;
&lt;p&gt;A huge thank you to the external contributors who helped during this release: @moko-poi, @janHildebrandt98, @Reranko05, @prakharbirla-ng, @esquonk, @chrismooreproductions, @vortegatorres, @SAY-5, and @LBaronceli! 🙏&lt;/p&gt;
]]></content><description>&lt;p>k6 &lt;code>v1.8.0&lt;/code> is here! This maintenance release on v1.x includes:&lt;/p>
&lt;ul>
&lt;li>Cloud secrets are now automatically available in &lt;code>k6 cloud run --local-execution&lt;/code> — use &lt;code>--no-cloud-secrets&lt;/code> to opt out.&lt;/li>
&lt;li>Pre-manifest extension dependencies are captured in archive metadata for cloud consumers.&lt;/li>
&lt;li>Structured logging from k6provider for better visibility into automatic extension provisioning.&lt;/li>
&lt;li>Multiple bug fixes for Browser, WebSocket, and Cloud modules.&lt;/li>
&lt;li>Dependency updates and reliability improvements.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>There are no breaking changes in this release.&lt;/p></description></item><item><title/><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.8.1/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v1.8.1/</guid><content><![CDATA[&lt;p&gt;k6 &lt;code&gt;v1.8.1&lt;/code&gt; is here! This patch release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bug fix for the cloud secret source being enabled by default, which broke scripts that configure their own secret source&lt;/li&gt;
&lt;li&gt;Bug fix for HTTP/2 negotiation and error classification under Go 1.27&lt;/li&gt;
&lt;li&gt;Security updates for gRPC, &lt;code&gt;golang.org/x/net&lt;/code&gt;, &lt;code&gt;golang.org/x/text&lt;/code&gt;, &lt;code&gt;golang.org/x/crypto&lt;/code&gt;, OpenTelemetry, &lt;code&gt;klauspost/compress&lt;/code&gt;, and the Go toolchain&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6274&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6274&lt;/a&gt; Stops enabling the Grafana Cloud secret source by default in &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;, reverting &lt;a href=&#34;https://github.com/grafana/k6/pull/5875&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5875&lt;/a&gt;. Scripts that configured their own secret source failed with &lt;code&gt;no secret source with name &amp;quot;default&amp;quot; is configured&lt;/code&gt;. Explicitly configured secret sources now work again as they did in &lt;code&gt;v1.7&lt;/code&gt;. Fixes &lt;a href=&#34;https://github.com/grafana/k6/issues/6093&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6093&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6275&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6275&lt;/a&gt; Fixes HTTP/2 negotiation and error classification under Go 1.27, which changed HTTP/2 negotiation and error behaviour. Keeps VUs on HTTP/2, classifies connection and &lt;code&gt;GOAWAY&lt;/code&gt; errors consistently across Go versions, and preserves unknown HTTP/2 error buckets. Backport of &lt;a href=&#34;https://github.com/grafana/k6/pull/6234&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6234&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-security-updates&#34;&gt;Maintenance and security updates&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6193&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6193&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6194&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6194&lt;/a&gt; Updates &lt;code&gt;google.golang.org/grpc&lt;/code&gt; to &lt;code&gt;v1.82.1&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6187&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6187&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6189&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6189&lt;/a&gt; Updates &lt;code&gt;golang.org/x/net&lt;/code&gt; to &lt;code&gt;v0.56.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6188&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6188&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6190&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6190&lt;/a&gt; Updates &lt;code&gt;golang.org/x/text&lt;/code&gt; to &lt;code&gt;v0.39.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6207&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6207&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel&lt;/code&gt; to &lt;code&gt;v1.44.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6220&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6220&lt;/a&gt; Updates &lt;code&gt;github.com/klauspost/compress&lt;/code&gt; to &lt;code&gt;v1.18.7&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6135&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6135&lt;/a&gt; Updates the Go toolchain to &lt;code&gt;v1.25.12&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;Updates &lt;code&gt;golang.org/x/crypto&lt;/code&gt; to &lt;code&gt;v0.53.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;p>k6 &lt;code>v1.8.1&lt;/code> is here! This patch release includes:&lt;/p>
&lt;ul>
&lt;li>Bug fix for the cloud secret source being enabled by default, which broke scripts that configure their own secret source&lt;/li>
&lt;li>Bug fix for HTTP/2 negotiation and error classification under Go 1.27&lt;/li>
&lt;li>Security updates for gRPC, &lt;code>golang.org/x/net&lt;/code>, &lt;code>golang.org/x/text&lt;/code>, &lt;code>golang.org/x/crypto&lt;/code>, OpenTelemetry, &lt;code>klauspost/compress&lt;/code>, and the Go toolchain&lt;/li>
&lt;/ul>
&lt;h2 id="bug-fixes">Bug fixes&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6274" target="_blank" rel="noopener noreferrer">#6274&lt;/a> Stops enabling the Grafana Cloud secret source by default in &lt;code>k6 cloud run --local-execution&lt;/code>, reverting &lt;a href="https://github.com/grafana/k6/pull/5875" target="_blank" rel="noopener noreferrer">#5875&lt;/a>. Scripts that configured their own secret source failed with &lt;code>no secret source with name &amp;quot;default&amp;quot; is configured&lt;/code>. Explicitly configured secret sources now work again as they did in &lt;code>v1.7&lt;/code>. Fixes &lt;a href="https://github.com/grafana/k6/issues/6093" target="_blank" rel="noopener noreferrer">#6093&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6275" target="_blank" rel="noopener noreferrer">#6275&lt;/a> Fixes HTTP/2 negotiation and error classification under Go 1.27, which changed HTTP/2 negotiation and error behaviour. Keeps VUs on HTTP/2, classifies connection and &lt;code>GOAWAY&lt;/code> errors consistently across Go versions, and preserves unknown HTTP/2 error buckets. Backport of &lt;a href="https://github.com/grafana/k6/pull/6234" target="_blank" rel="noopener noreferrer">#6234&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="maintenance-and-security-updates">Maintenance and security updates&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6193" target="_blank" rel="noopener noreferrer">#6193&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/6194" target="_blank" rel="noopener noreferrer">#6194&lt;/a> Updates &lt;code>google.golang.org/grpc&lt;/code> to &lt;code>v1.82.1&lt;/code> [security].&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6187" target="_blank" rel="noopener noreferrer">#6187&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/6189" target="_blank" rel="noopener noreferrer">#6189&lt;/a> Updates &lt;code>golang.org/x/net&lt;/code> to &lt;code>v0.56.0&lt;/code> [security].&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6188" target="_blank" rel="noopener noreferrer">#6188&lt;/a>, &lt;a href="https://github.com/grafana/k6/pull/6190" target="_blank" rel="noopener noreferrer">#6190&lt;/a> Updates &lt;code>golang.org/x/text&lt;/code> to &lt;code>v0.39.0&lt;/code> [security].&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6207" target="_blank" rel="noopener noreferrer">#6207&lt;/a> Updates &lt;code>go.opentelemetry.io/otel&lt;/code> to &lt;code>v1.44.0&lt;/code> [security].&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6220" target="_blank" rel="noopener noreferrer">#6220&lt;/a> Updates &lt;code>github.com/klauspost/compress&lt;/code> to &lt;code>v1.18.7&lt;/code> [security].&lt;/li>
&lt;li>&lt;a href="https://github.com/grafana/k6/pull/6135" target="_blank" rel="noopener noreferrer">#6135&lt;/a> Updates the Go toolchain to &lt;code>v1.25.12&lt;/code> [security].&lt;/li>
&lt;li>Updates &lt;code>golang.org/x/crypto&lt;/code> to &lt;code>v0.53.0&lt;/code> [security].&lt;/li>
&lt;/ul></description></item><item><title/><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.0.0-rc1/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.0.0-rc1/</guid><content><![CDATA[&lt;p&gt;k6 &lt;code&gt;v2.0.0-rc1&lt;/code&gt; is here 🎉!&lt;/p&gt;
&lt;p&gt;This release marks the first release candidate for k6 v2.0.0 — a major version that completes a long-running cleanup of deprecated APIs, old commands, and obsolete configuration options. Like the v1.0.0-rc1 before it, the purpose of this release is to give the community a chance to test the upgrade path, identify any issues, and migrate scripts or workflows affected by breaking changes. If you encounter any problems, please &lt;a href=&#34;https://github.com/grafana/k6/issues&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;report them&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a glimpse of what&amp;rsquo;s changed in this release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Go module path has changed to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt; — all extensions must update their import paths to be compatible with v2.&lt;/li&gt;
&lt;li&gt;Removal of all long-deprecated CLI commands and flags: &lt;code&gt;k6 login&lt;/code&gt;, &lt;code&gt;k6 pause&lt;/code&gt;, &lt;code&gt;k6 resume&lt;/code&gt;, &lt;code&gt;k6 scale&lt;/code&gt;, &lt;code&gt;k6 status&lt;/code&gt;, &lt;code&gt;--no-summary&lt;/code&gt;, &lt;code&gt;--upload-only&lt;/code&gt;, and more.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;externally-controlled&lt;/code&gt; executor has been removed — scripts using &lt;code&gt;executor: externally-controlled&lt;/code&gt; will no longer run.&lt;/li&gt;
&lt;li&gt;Cloud run non-threshold aborts (aborted by user, system, timeout, etc.) now return exit code &lt;code&gt;97&lt;/code&gt; instead of &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.ext.loadimpact&lt;/code&gt; is no longer supported — use &lt;code&gt;options.cloud&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6/experimental/redis&lt;/code&gt; module has been removed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;k6 cloud&lt;/code&gt; command now shows help by default instead of attempting to run.&lt;/li&gt;
&lt;li&gt;A stack is now required for all &lt;code&gt;k6 cloud&lt;/code&gt; commands — the previous fallback to the first available stack has been removed.&lt;/li&gt;
&lt;li&gt;The web-vitals library has been updated to v5.1.0, removing the deprecated FID metric.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;These are changes that require you to update your scripts, CI/CD pipelines, or configuration files before upgrading.&lt;/p&gt;
&lt;h3 id=&#34;go-module-path-changed-to-gok6iok6v2-5777&#34;&gt;Go module path changed to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5777&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5777&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Following the &lt;a href=&#34;https://go.dev/ref/mod#major-version-suffixes&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Go module versioning conventions&lt;/a&gt;, the k6 module path has changed from &lt;code&gt;go.k6.io/k6&lt;/code&gt; to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any extension or external package that imports &lt;code&gt;go.k6.io/k6&lt;/code&gt; must update all import paths to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt;. For the vast majority of extensions this is the only change needed — a mechanical find-and-replace across the codebase:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;go.k6.io/k6/ → go.k6.io/k6/v2/&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Go&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-go&#34;&gt;// Before
import &amp;#34;go.k6.io/k6/js/modules&amp;#34;

// After
import &amp;#34;go.k6.io/k6/v2/js/modules&amp;#34;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;As a result, &lt;strong&gt;no existing extensions will be compatible with v2.0.0-rc1&lt;/strong&gt;. As we move toward the final v2.0.0 release, we expect most extensions to either already support the new path or to do so shortly after.&lt;/p&gt;
&lt;h3 id=&#34;removed-cli-commands-5653&#34;&gt;Removed CLI commands &lt;a href=&#34;https://github.com/grafana/k6/pull/5653&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5653&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following commands for controlling a running test have been removed. They have not been functional for most use cases since the REST API they relied on was limited to specific execution modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k6 pause&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6 resume&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6 scale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6 status&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; There is no replacement. These commands relied on the &lt;code&gt;externally-controlled&lt;/code&gt; executor, which has also been removed in v2.0.0 (see below).&lt;/p&gt;
&lt;h3 id=&#34;removed-externally-controlled-executor-5846&#34;&gt;Removed &lt;code&gt;externally-controlled&lt;/code&gt; executor &lt;a href=&#34;https://github.com/grafana/k6/pull/5846&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5846&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;externally-controlled&lt;/code&gt; executor has been removed. It was legacy code from an older k6 Cloud architecture that allowed external systems to scale VUs and pause/resume a running test via the k6 REST API — the capability that &lt;code&gt;k6 pause&lt;/code&gt;, &lt;code&gt;k6 resume&lt;/code&gt;, &lt;code&gt;k6 scale&lt;/code&gt;, and &lt;code&gt;k6 status&lt;/code&gt; relied on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; There is no replacement. Any test script with &lt;code&gt;executor: externally-controlled&lt;/code&gt; will fail to start. Migrate to a different executor based on the desired load profile (e.g., &lt;code&gt;ramping-vus&lt;/code&gt;, &lt;code&gt;constant-vus&lt;/code&gt;, &lt;code&gt;constant-arrival-rate&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&#34;removed-k6-login-command-5134&#34;&gt;Removed &lt;code&gt;k6 login&lt;/code&gt; command &lt;a href=&#34;https://github.com/grafana/k6/pull/5134&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5134&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The top-level &lt;code&gt;k6 login&lt;/code&gt; command and its subcommands (&lt;code&gt;k6 login cloud&lt;/code&gt;, &lt;code&gt;k6 login influxdb&lt;/code&gt;) have been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;k6 login cloud&lt;/code&gt; → &lt;code&gt;k6 cloud login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;InfluxDB authentication is no longer configurable via a login command. Use environment variables such as &lt;code&gt;K6_INFLUXDB_*&lt;/code&gt; to configure the InfluxDB output directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;k6-cloud-scriptjs-no-longer-runs-a-test-5624&#34;&gt;&lt;code&gt;k6 cloud script.js&lt;/code&gt; no longer runs a test &lt;a href=&#34;https://github.com/grafana/k6/pull/5624&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5624&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Running &lt;code&gt;k6 cloud&lt;/code&gt; without an explicit subcommand (i.e., the old positional-argument form &lt;code&gt;k6 cloud script.js&lt;/code&gt;) now shows the help output instead of attempting to run the test. The &lt;code&gt;run&lt;/code&gt; subcommand was already the recommended path since &lt;code&gt;k6 cloud run&lt;/code&gt; was introduced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Replace &lt;code&gt;k6 cloud script.js&lt;/code&gt; with &lt;code&gt;k6 cloud run script.js&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;removed---upload-only-flag-5844&#34;&gt;Removed &lt;code&gt;--upload-only&lt;/code&gt; flag &lt;a href=&#34;https://github.com/grafana/k6/pull/5844&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5844&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--upload-only&lt;/code&gt; flag on the &lt;code&gt;k6 cloud&lt;/code&gt; command has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Use &lt;code&gt;k6 cloud upload script.js&lt;/code&gt; to upload a test without running it.&lt;/p&gt;
&lt;h3 id=&#34;removed---no-summary-flag-5729&#34;&gt;Removed &lt;code&gt;--no-summary&lt;/code&gt; flag &lt;a href=&#34;https://github.com/grafana/k6/pull/5729&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5729&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--no-summary&lt;/code&gt; flag has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Replace &lt;code&gt;--no-summary&lt;/code&gt; with &lt;code&gt;--summary-mode=disabled&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;removed---summary-modelegacy-5730&#34;&gt;Removed &lt;code&gt;--summary-mode=legacy&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5730&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5730&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;legacy&lt;/code&gt; value for &lt;code&gt;--summary-mode&lt;/code&gt; has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; There is no direct equivalent — the new summary format is different from the legacy one. Review the available &lt;a href=&#34;/docs/k6/latest/results-output/end-of-test/#summary-modes&#34;&gt;summary modes&lt;/a&gt; and choose the one that best fits your needs: &lt;code&gt;compact&lt;/code&gt; (the default) or &lt;code&gt;full&lt;/code&gt; for more detailed output.&lt;/p&gt;
&lt;h3 id=&#34;removed-optionsextloadimpact-support-5774&#34;&gt;Removed &lt;code&gt;options.ext.loadimpact&lt;/code&gt; support &lt;a href=&#34;https://github.com/grafana/k6/pull/5774&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5774&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;options.ext.loadimpact&lt;/code&gt; configuration block in test scripts is no longer supported.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Move all cloud-related configuration from &lt;code&gt;options.ext.loadimpact&lt;/code&gt; to &lt;code&gt;options.cloud&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Before
export const options = {
  ext: {
    loadimpact: {
      projectID: 12345,
      name: &amp;#34;My Test&amp;#34;,
    },
  },
};
export default () =&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// After
export const options = {
  cloud: {
    projectID: 12345,
    name: &amp;#34;My Test&amp;#34;,
  },
};
export default () =&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;removed-k6experimentalredis-module-5485&#34;&gt;Removed &lt;code&gt;k6/experimental/redis&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/k6/issues/5485&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5485&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;k6/experimental/redis&lt;/code&gt; module has been removed from the k6 core binary. It was shipped as an experiment and has not been promoted to stable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Change your import from &lt;code&gt;k6/experimental/redis&lt;/code&gt; to &lt;code&gt;k6/x/redis&lt;/code&gt;. With &lt;a href=&#34;/docs/k6/latest/using-k6/auto-extension-resolution/&#34;&gt;auto-extension-resolution&lt;/a&gt;, k6 will automatically provision the &lt;a href=&#34;https://github.com/grafana/xk6-redis&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-redis&lt;/a&gt; extension when it sees the &lt;code&gt;k6/x/redis&lt;/code&gt; import.&lt;/p&gt;
&lt;h3 id=&#34;removed-exportertype-option-from-opentelemetry-output-5754&#34;&gt;Removed &lt;code&gt;ExporterType&lt;/code&gt; option from OpenTelemetry output &lt;a href=&#34;https://github.com/grafana/k6/pull/5754&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5754&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The deprecated &lt;code&gt;exporterType&lt;/code&gt; configuration option for the OpenTelemetry output has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Replace &lt;code&gt;K6_OTEL_EXPORTER_TYPE&lt;/code&gt; with &lt;code&gt;K6_OTEL_EXPORTER_PROTOCOL&lt;/code&gt;. The accepted values are &lt;code&gt;grpc&lt;/code&gt; and &lt;code&gt;http/protobuf&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;removed-singlecounterforrate-option-from-opentelemetry-output-5830&#34;&gt;Removed &lt;code&gt;SingleCounterForRate&lt;/code&gt; option from OpenTelemetry output &lt;a href=&#34;https://github.com/grafana/k6/pull/5830&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5830&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The temporary &lt;code&gt;SingleCounterForRate&lt;/code&gt; escape-hatch option for the OpenTelemetry output has been removed. It was introduced as a one-release migration aid in &lt;a href=&#34;https://github.com/grafana/k6/pull/5164&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5164&lt;/a&gt; to let users revert to the old pair-of-counters format (&lt;code&gt;&amp;lt;metric&amp;gt;.occurred&lt;/code&gt; &#43; &lt;code&gt;&amp;lt;metric&amp;gt;.total&lt;/code&gt;) while upgrading. Rate metrics are now always exported as a single counter with a &lt;code&gt;condition&lt;/code&gt; attribute (&lt;code&gt;nonzero&lt;/code&gt;/&lt;code&gt;zero&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Remove any &lt;code&gt;K6_OTEL_SINGLE_COUNTER_FOR_RATE=true&lt;/code&gt; configuration. If you were using the old pair-of-counters format, update your dashboards and queries to use the single counter with &lt;code&gt;condition&lt;/code&gt; attribute instead.&lt;/p&gt;
&lt;h3 id=&#34;removed-k6_binary_provisioning-environment-variable-5734&#34;&gt;Removed &lt;code&gt;K6_BINARY_PROVISIONING&lt;/code&gt; environment variable &lt;a href=&#34;https://github.com/grafana/k6/pull/5734&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5734&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;K6_BINARY_PROVISIONING&lt;/code&gt; environment variable, deprecated in v1.2.0, has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Remove &lt;code&gt;K6_BINARY_PROVISIONING&lt;/code&gt; from your environment. Auto-extension-resolution is enabled by default; &lt;code&gt;K6_AUTO_EXTENSION_RESOLUTION&lt;/code&gt; only needs to be set explicitly if you want to disable it.&lt;/p&gt;
&lt;h3 id=&#34;removed-k6_enable_community_extensions-environment-variable-5733&#34;&gt;Removed &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; environment variable &lt;a href=&#34;https://github.com/grafana/k6/pull/5733&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5733&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; environment variable has been removed. The community and cloud extension catalogs were merged server-side, making this flag a no-op since the catalogs were unified.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Remove &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; from your environment. Community extensions are now resolved through the default build service URL automatically.&lt;/p&gt;
&lt;h3 id=&#34;stack-is-now-required-for-all-k6-cloud-commands-5833&#34;&gt;Stack is now required for all &lt;code&gt;k6 cloud&lt;/code&gt; commands &lt;a href=&#34;https://github.com/grafana/k6/pull/5833&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5833&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Providing a stack is now mandatory for all &lt;code&gt;k6 cloud&lt;/code&gt; commands (&lt;code&gt;k6 cloud run&lt;/code&gt;, &lt;code&gt;k6 cloud upload&lt;/code&gt;, &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;). Previously, omitting a stack would fall back to the first available stack with a deprecation warning — that fallback has been removed. Likewise, &lt;code&gt;k6 cloud login&lt;/code&gt; now requires both a token and a stack; passing one without the other fails with an explicit error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Ensure a stack is always provided via &lt;code&gt;k6 cloud login --stack&lt;/code&gt; passing the stack slug or the full URL. Alternatively, the &lt;code&gt;K6_CLOUD_STACK_ID&lt;/code&gt; environment variable, or the &lt;code&gt;stackID&lt;/code&gt; script option are available to be set before running any &lt;code&gt;k6 cloud&lt;/code&gt; command .&lt;/p&gt;
&lt;h3 id=&#34;removed-legacy-configuration-file-path-migration-5609&#34;&gt;Removed legacy configuration file path migration &lt;a href=&#34;https://github.com/grafana/k6/pull/5609&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5609&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 no longer automatically migrates configuration files from the old &lt;code&gt;{USER_CONFIG_DIR}/loadimpact/config.json&lt;/code&gt; path introduced before k6 v1.0.0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; If you still have a config file at the old path, move it to &lt;code&gt;{USER_CONFIG_DIR}/k6/config.json&lt;/code&gt;. You can also re-run &lt;code&gt;k6 cloud login&lt;/code&gt; to regenerate the file at the correct location.&lt;/p&gt;
&lt;h3 id=&#34;cloud-run-non-threshold-aborts-now-exit-with-code-97-5769&#34;&gt;Cloud run non-threshold aborts now exit with code 97 &lt;a href=&#34;https://github.com/grafana/k6/pull/5769&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5769&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Previously, most cloud abort scenarios returned an exit code of &lt;code&gt;0&lt;/code&gt;, making it impossible for CI pipelines to distinguish a failed run from a successful one. These scenarios now return exit code &lt;code&gt;97&lt;/code&gt;.&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Scenario&lt;/th&gt;
              &lt;th&gt;Before&lt;/th&gt;
              &lt;th&gt;After&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Finished&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Aborted by threshold&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;99&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;99&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Aborted by system, limit, script error, user, or timeout&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;97&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Review any CI pipeline logic that treats exit code &lt;code&gt;0&lt;/code&gt; as success for cloud runs. Cloud runs that abort for reasons other than threshold violations will now return exit code &lt;code&gt;97&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;browser-fid-metric-removed-web-vitals-updated-to-v510-5661&#34;&gt;Browser: FID metric removed, web vitals updated to v5.1.0 &lt;a href=&#34;https://github.com/grafana/k6/pull/5661&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5661&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;browser_web_vital_fid&lt;/code&gt; metric has been removed. FID (First Input Delay) was deprecated as a Core Web Vital and replaced by INP (Interaction to Next Paint). The embedded web-vitals library has been updated from v3 to v5.1.0, which reflects this change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Update any dashboards, alerts, or threshold rules that reference &lt;code&gt;browser_web_vital_fid&lt;/code&gt; to use &lt;code&gt;browser_web_vital_inp&lt;/code&gt; instead.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;cloud-secret-source-5738&#34;&gt;Cloud secret source &lt;a href=&#34;https://github.com/grafana/k6/pull/5738&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5738&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new built-in secret source, &lt;code&gt;cloud&lt;/code&gt;, allows k6 scripts to retrieve secrets stored in Grafana Cloud directly during local execution. Pass &lt;code&gt;--secret-source=cloud&lt;/code&gt; when running &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; to make cloud-managed secrets available to your script via &lt;code&gt;secrets.get()&lt;/code&gt;. Thanks, @vortegatorres!&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud run --local-execution --secret-source=cloud script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;extension-tab-completion-provisioning-5761&#34;&gt;Extension tab-completion provisioning &lt;a href=&#34;https://github.com/grafana/k6/pull/5761&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5761&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When using shell tab-completion, pressing TAB after a fully-typed extension name (e.g. &lt;code&gt;k6 x docs &amp;lt;TAB&amp;gt;&lt;/code&gt;) will now automatically provision a custom binary with that extension and delegate the completion request to it. This means extension-specific flags and subcommands are available via tab-completion without any manual setup.&lt;/p&gt;
&lt;h3 id=&#34;pre-manifest-extension-dependencies-captured-in-archive-metadata-5819&#34;&gt;Pre-manifest extension dependencies captured in archive metadata &lt;a href=&#34;https://github.com/grafana/k6/pull/5819&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5819&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When creating a k6 archive (&lt;code&gt;k6 archive&lt;/code&gt;), extension dependency information is now captured in &lt;code&gt;metadata.json&lt;/code&gt; under a &lt;code&gt;&amp;quot;dependencies&amp;quot;&lt;/code&gt; field, using the constraints declared by the script before any external manifest overrides are applied. This ensures that &lt;code&gt;k6/x/&lt;/code&gt; imports are preserved correctly during &lt;a href=&#34;/docs/k6/latest/using-k6/auto-extension-resolution/&#34;&gt;auto-extension-resolution&lt;/a&gt; re-execution.&lt;/p&gt;
&lt;h2 id=&#34;ux-improvements-and-enhancements&#34;&gt;UX improvements and enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5815&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5815&lt;/a&gt; Warns when &lt;code&gt;http.get()&lt;/code&gt; or &lt;code&gt;http.head()&lt;/code&gt; receive extra arguments that are silently ignored, helping catch common scripting mistakes. Thanks, @moko-poi!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5657&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5657&lt;/a&gt; Adds automatic retries to &lt;code&gt;newAction&lt;/code&gt;-based Locator APIs in the browser module, improving reliability of browser tests. Thanks, @janHildebrandt98!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5779&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5779&lt;/a&gt; Adds a &lt;code&gt;k6 x docs&lt;/code&gt; hint to the main k6 help output for built-in documentation discovery. Thanks, @Reranko05!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5788&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5788&lt;/a&gt; Adds a &lt;code&gt;k6 x explore&lt;/code&gt; hint to the main k6 help output for extension discovery.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5855&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5855&lt;/a&gt; Fixes auto-extension-resolution incorrectly triggering binary provisioning when a script manifest specifies a dependency as a Go module pseudo-version (&lt;code&gt;v0.0.0&#43;sha&lt;/code&gt; or &lt;code&gt;v0.0.0-timestamp-sha&lt;/code&gt;), even when the running binary already satisfied the constraint.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5769&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5769&lt;/a&gt; Fixes several &lt;code&gt;k6 cloud run&lt;/code&gt; TUI issues: ghost duplicate progress bars appearing after the run finishes, a timer that stalled and then jumped to 100%, and a stale timer racing with the status line.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5814&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5814&lt;/a&gt; Fixes &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; ignoring &lt;code&gt;K6_CLOUD_PUSH_REF_ID&lt;/code&gt; and unconditionally creating a new test run instead of reusing the provided run ID. Thanks, @Reranko05!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5716&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5716&lt;/a&gt; Fixes WebSocket &lt;code&gt;bufferedAmount&lt;/code&gt; not being incremented when sending TypedArrays, causing it to go negative. Thanks, @prakharbirla-ng!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5630&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5630&lt;/a&gt; Fixes duplicate redirect request metric emissions in the browser module, where each redirect was incorrectly emitting metrics for all prior redirects in the chain.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5786&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5786&lt;/a&gt; Fixes swapped Min and Max values for Gauge metrics in Cloud output v2, which caused incorrect peak and floor values in cloud test result queries. Thanks, @esquonk!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5785&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5785&lt;/a&gt; Fixes a race condition in the browser module&amp;rsquo;s &lt;code&gt;handleExitEvent&lt;/code&gt; where &lt;code&gt;Done()&lt;/code&gt; was signalled before the subscription was removed, causing tests to hang until the timeout.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5574&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5574&lt;/a&gt; Fixes position parser for base pointer options. Thanks, @chrismooreproductions!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-internal-improvements&#34;&gt;Maintenance and internal improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5847&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5847&lt;/a&gt; Moves &lt;code&gt;TaskQueue&lt;/code&gt; from the external &lt;code&gt;github.com/mstoykov/k6-taskqueue-lib&lt;/code&gt; into &lt;code&gt;internal/js/taskqueue&lt;/code&gt;, eliminating an awkward reverse dependency where the library&amp;rsquo;s own tests depended on k6.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5775&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5775&lt;/a&gt; Moves xk6-dashboard source code into &lt;code&gt;internal/dashboard&lt;/code&gt;, making the web dashboard a built-in part of the k6 binary without requiring a separate extension. The dashboard is available via &lt;code&gt;k6 run --out=web-dashboard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5703&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5703&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5818&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5818&lt;/a&gt; Uses Go&amp;rsquo;s &lt;code&gt;synctest&lt;/code&gt; package within &lt;code&gt;lib/executor&lt;/code&gt;, eventloop, and timer tests for virtualized time, improving test determinism.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5778&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5778&lt;/a&gt; Bumps Ubuntu CI runners to 24.04 and always installs Chromium for browser tests.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5826&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5826&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5827&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5827&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel/exporters/otlp/otlptracehttp&lt;/code&gt; and &lt;code&gt;otlpmetrichttp&lt;/code&gt; to &lt;code&gt;v1.43.0&lt;/code&gt; [security], adding a 4 MiB response body cap to mitigate memory exhaustion from misconfigured or malicious servers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5669&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5669&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel&lt;/code&gt; packages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5740&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5740&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5796&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5796&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/sobek&lt;/code&gt; digest.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5783&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5783&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5838&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5838&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5862&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5862&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/k6-cloud-openapi-client-go&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5407&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5407&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5801&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5801&lt;/a&gt; Updates &lt;code&gt;github.com/mailru/easyjson&lt;/code&gt; to &lt;code&gt;v0.9.2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5616&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5616&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5805&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5805&lt;/a&gt; Updates &lt;code&gt;github.com/evanw/esbuild&lt;/code&gt; to &lt;code&gt;v0.28.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5667&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5667&lt;/a&gt; Updates golangci-lint.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5799&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5799&lt;/a&gt; Updates &lt;code&gt;buf.build/gen/go/prometheus&lt;/code&gt; protobuf.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5800&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5800&lt;/a&gt; Updates &lt;code&gt;github.com/andybalholm/brotli&lt;/code&gt; to &lt;code&gt;v1.2.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5804&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5804&lt;/a&gt; Updates &lt;code&gt;examples/&lt;/code&gt; dependencies.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5806&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5806&lt;/a&gt; Updates &lt;code&gt;github.com/fatih/color&lt;/code&gt; to &lt;code&gt;v1.19.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5807&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5807&lt;/a&gt; Updates &lt;code&gt;github.com/puerkitobio/goquery&lt;/code&gt; to &lt;code&gt;v1.12.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5840&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5840&lt;/a&gt; Updates &lt;code&gt;github.com/mattn/go-isatty&lt;/code&gt; to &lt;code&gt;v0.0.21&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5857&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5857&lt;/a&gt; Updates Go to &lt;code&gt;v1.25.9&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5863&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5863&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5880&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5880&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/k6provider&lt;/code&gt; to &lt;code&gt;v0.4.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5670&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5670&lt;/a&gt; Updates GitHub artifact actions (major).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5693&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5693&lt;/a&gt; Updates &lt;code&gt;crazy-max/ghaction-chocolatey&lt;/code&gt; to &lt;code&gt;v4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5706&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5706&lt;/a&gt; Updates &lt;code&gt;actions/setup-go&lt;/code&gt; to &lt;code&gt;4a36011&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5707&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5707&lt;/a&gt; Updates &lt;code&gt;anchore/sbom-action&lt;/code&gt; to &lt;code&gt;v0.24.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5718&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5718&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5861&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5861&lt;/a&gt; Updates &lt;code&gt;github/codeql-action&lt;/code&gt; to &lt;code&gt;v4.35.2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5720&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5720&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5802&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5802&lt;/a&gt; Updates &lt;code&gt;docker/login-action&lt;/code&gt; to &lt;code&gt;v4.1.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5771&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5771&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5798&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5798&lt;/a&gt; Updates &lt;code&gt;grafana/shared-workflows&lt;/code&gt; actions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5836&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5836&lt;/a&gt; Updates &lt;code&gt;actions/upload-artifact&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5809&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5809&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5839&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5839&lt;/a&gt; Updates the Docker base image tags (Debian to v13, Go to &lt;code&gt;v1.26.2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5872&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5872&lt;/a&gt; Fixes v6 cloud client losing the request body when retrying after a &lt;code&gt;Connection: close&lt;/code&gt; response.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5871&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5871&lt;/a&gt; Returns an accurate error from the v6 cloud client when a test name lookup returns no results.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5751&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5751&lt;/a&gt; Updates &lt;code&gt;github.com/klauspost/compress&lt;/code&gt; to &lt;code&gt;v1.18.5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5841&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5841&lt;/a&gt; Updates &lt;code&gt;golang.org/x&lt;/code&gt; packages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5860&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5860&lt;/a&gt; Updates Alpine Docker image to &lt;code&gt;v3.23.4&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;external-contributors&#34;&gt;External contributors&lt;/h2&gt;
&lt;p&gt;A huge thank you to the external contributors who helped during this release: @moko-poi, @janHildebrandt98, @Reranko05, @prakharbirla-ng, and @chrismooreproductions! 🙏&lt;/p&gt;
]]></content><description>&lt;p>k6 &lt;code>v2.0.0-rc1&lt;/code> is here 🎉!&lt;/p>
&lt;p>This release marks the first release candidate for k6 v2.0.0 — a major version that completes a long-running cleanup of deprecated APIs, old commands, and obsolete configuration options. Like the v1.0.0-rc1 before it, the purpose of this release is to give the community a chance to test the upgrade path, identify any issues, and migrate scripts or workflows affected by breaking changes. If you encounter any problems, please &lt;a href="https://github.com/grafana/k6/issues" target="_blank" rel="noopener noreferrer">report them&lt;/a>.&lt;/p></description></item><item><title/><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.0.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.0.0/</guid><content><![CDATA[&lt;p&gt;k6 &lt;code&gt;v2.0.0&lt;/code&gt; is here 🎉!&lt;/p&gt;
&lt;p&gt;k6 v2.0.0 is the final release of the v2 major version, completing the cleanup of deprecated APIs, old commands, and obsolete configuration options that was started with v2.0.0-rc1. If you were already running the release candidate, this release includes a handful of additional changes on top — they are marked with &lt;strong&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt;&lt;/strong&gt; throughout these notes.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a glimpse of what&amp;rsquo;s changed in this release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Go module path has changed to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt; — all extensions must update their import paths to be compatible with v2.&lt;/li&gt;
&lt;li&gt;Removal of all long-deprecated CLI commands and flags: &lt;code&gt;k6 login&lt;/code&gt;, &lt;code&gt;k6 pause&lt;/code&gt;, &lt;code&gt;k6 resume&lt;/code&gt;, &lt;code&gt;k6 scale&lt;/code&gt;, &lt;code&gt;k6 status&lt;/code&gt;, &lt;code&gt;--no-summary&lt;/code&gt;, &lt;code&gt;--upload-only&lt;/code&gt;, and more.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;externally-controlled&lt;/code&gt; executor has been removed — scripts using &lt;code&gt;executor: externally-controlled&lt;/code&gt; will no longer run.&lt;/li&gt;
&lt;li&gt;Cloud run non-threshold aborts (aborted by user, system, timeout, etc.) now return exit code &lt;code&gt;97&lt;/code&gt; instead of &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.ext.loadimpact&lt;/code&gt; is no longer supported — use &lt;code&gt;options.cloud&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6/experimental/redis&lt;/code&gt; module has been removed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;k6 cloud script.js&lt;/code&gt; positional form has been fully removed — use &lt;code&gt;k6 cloud run script.js&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A stack is now required for all &lt;code&gt;k6 cloud&lt;/code&gt; commands — the previous fallback to the first available stack has been removed.&lt;/li&gt;
&lt;li&gt;The web-vitals library has been updated to v5.1.0, removing the deprecated FID metric.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt;&lt;/strong&gt; easyjson has been dropped in favor of stdlib &lt;code&gt;encoding/json&lt;/code&gt; — extension authors relying on easyjson-generated methods on k6 types must update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt;&lt;/strong&gt; The k6 HTTP API server no longer starts by default — pass &lt;code&gt;--address&lt;/code&gt; to enable it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt;&lt;/strong&gt; New &lt;code&gt;k6 cloud project list&lt;/code&gt; command to list Grafana Cloud k6 projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt;&lt;/strong&gt; Cloud secrets are now automatically available in &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;; use &lt;code&gt;--no-cloud-secrets&lt;/code&gt; to opt out.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;These are changes that require you to update your scripts, CI/CD pipelines, or configuration files before upgrading.&lt;/p&gt;
&lt;h3 id=&#34;go-module-path-changed-to-gok6iok6v2-5777&#34;&gt;Go module path changed to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5777&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5777&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Following the &lt;a href=&#34;https://go.dev/ref/mod#major-version-suffixes&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Go module versioning conventions&lt;/a&gt;, the k6 module path has changed from &lt;code&gt;go.k6.io/k6&lt;/code&gt; to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any extension or external package that imports &lt;code&gt;go.k6.io/k6&lt;/code&gt; must update all import paths to &lt;code&gt;go.k6.io/k6/v2&lt;/code&gt;. For the vast majority of extensions this is the only change needed — a mechanical find-and-replace across the codebase:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;go.k6.io/k6/ → go.k6.io/k6/v2/&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Go&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-go&#34;&gt;// Before
import &amp;#34;go.k6.io/k6/js/modules&amp;#34;

// After
import &amp;#34;go.k6.io/k6/v2/js/modules&amp;#34;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;removed-cli-commands-5653&#34;&gt;Removed CLI commands &lt;a href=&#34;https://github.com/grafana/k6/pull/5653&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5653&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following commands for controlling a running test have been removed. They have not been functional for most use cases since the REST API they relied on was limited to specific execution modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k6 pause&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6 resume&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6 scale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k6 status&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; There is no replacement. These commands relied on the &lt;code&gt;externally-controlled&lt;/code&gt; executor, which has also been removed in v2.0.0 (see below).&lt;/p&gt;
&lt;h3 id=&#34;removed-externally-controlled-executor-5846&#34;&gt;Removed &lt;code&gt;externally-controlled&lt;/code&gt; executor &lt;a href=&#34;https://github.com/grafana/k6/pull/5846&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5846&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;externally-controlled&lt;/code&gt; executor has been removed. It was legacy code from an older k6 Cloud architecture that allowed external systems to scale VUs and pause/resume a running test via the k6 REST API — the capability that &lt;code&gt;k6 pause&lt;/code&gt;, &lt;code&gt;k6 resume&lt;/code&gt;, &lt;code&gt;k6 scale&lt;/code&gt;, and &lt;code&gt;k6 status&lt;/code&gt; relied on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; There is no replacement. Any test script with &lt;code&gt;executor: externally-controlled&lt;/code&gt; will fail to start. Migrate to a different executor based on the desired load profile (e.g., &lt;code&gt;ramping-vus&lt;/code&gt;, &lt;code&gt;constant-vus&lt;/code&gt;, &lt;code&gt;constant-arrival-rate&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&#34;removed-k6-login-command-5134&#34;&gt;Removed &lt;code&gt;k6 login&lt;/code&gt; command &lt;a href=&#34;https://github.com/grafana/k6/pull/5134&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5134&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The top-level &lt;code&gt;k6 login&lt;/code&gt; command and its subcommands (&lt;code&gt;k6 login cloud&lt;/code&gt;, &lt;code&gt;k6 login influxdb&lt;/code&gt;) have been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;k6 login cloud&lt;/code&gt; → &lt;code&gt;k6 cloud login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;InfluxDB authentication is no longer configurable via a login command. Use environment variables such as &lt;code&gt;K6_INFLUXDB_*&lt;/code&gt; to configure the InfluxDB output directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;removed-k6-cloud-scriptjs-positional-form-5624-5912-completed-in-v200&#34;&gt;Removed &lt;code&gt;k6 cloud script.js&lt;/code&gt; positional form &lt;a href=&#34;https://github.com/grafana/k6/pull/5624&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5624&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5912&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5912&lt;/a&gt; &lt;em&gt;(completed in v2.0.0)&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;The old positional-argument form &lt;code&gt;k6 cloud script.js&lt;/code&gt; has been fully removed. In v2.0.0-rc1 it was changed to show help instead of running; in v2.0.0 the deprecated command handler itself has been removed entirely. The &lt;code&gt;run&lt;/code&gt; subcommand has been the recommended path since &lt;code&gt;k6 cloud run&lt;/code&gt; was introduced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Replace &lt;code&gt;k6 cloud script.js&lt;/code&gt; with &lt;code&gt;k6 cloud run script.js&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;removed---upload-only-flag-5844&#34;&gt;Removed &lt;code&gt;--upload-only&lt;/code&gt; flag &lt;a href=&#34;https://github.com/grafana/k6/pull/5844&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5844&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--upload-only&lt;/code&gt; flag on the &lt;code&gt;k6 cloud&lt;/code&gt; command has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Use &lt;code&gt;k6 cloud upload script.js&lt;/code&gt; to upload a test without running it.&lt;/p&gt;
&lt;h3 id=&#34;removed---no-summary-flag-5729&#34;&gt;Removed &lt;code&gt;--no-summary&lt;/code&gt; flag &lt;a href=&#34;https://github.com/grafana/k6/pull/5729&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5729&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--no-summary&lt;/code&gt; flag has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Replace &lt;code&gt;--no-summary&lt;/code&gt; with &lt;code&gt;--summary-mode=disabled&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;removed---summary-modelegacy-5730&#34;&gt;Removed &lt;code&gt;--summary-mode=legacy&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5730&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5730&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;legacy&lt;/code&gt; value for &lt;code&gt;--summary-mode&lt;/code&gt; has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; There is no direct equivalent — the new summary format is different from the legacy one. Review the available &lt;a href=&#34;/docs/k6/latest/results-output/end-of-test/#summary-modes&#34;&gt;summary modes&lt;/a&gt; and choose the one that best fits your needs: &lt;code&gt;compact&lt;/code&gt; (the default) or &lt;code&gt;full&lt;/code&gt; for more detailed output.&lt;/p&gt;
&lt;h3 id=&#34;removed-optionsextloadimpact-support-5774&#34;&gt;Removed &lt;code&gt;options.ext.loadimpact&lt;/code&gt; support &lt;a href=&#34;https://github.com/grafana/k6/pull/5774&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5774&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;options.ext.loadimpact&lt;/code&gt; configuration block in test scripts is no longer supported.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Move all cloud-related configuration from &lt;code&gt;options.ext.loadimpact&lt;/code&gt; to &lt;code&gt;options.cloud&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// Before
export const options = {
  ext: {
    loadimpact: {
      projectID: 12345,
      name: &amp;#34;My Test&amp;#34;,
    },
  },
};
export default () =&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;// After
export const options = {
  cloud: {
    projectID: 12345,
    name: &amp;#34;My Test&amp;#34;,
  },
};
export default () =&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;removed-k6experimentalredis-module-5485&#34;&gt;Removed &lt;code&gt;k6/experimental/redis&lt;/code&gt; module &lt;a href=&#34;https://github.com/grafana/k6/issues/5485&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5485&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;k6/experimental/redis&lt;/code&gt; module has been removed from the k6 core binary. It was shipped as an experiment and has not been promoted to stable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Change your import from &lt;code&gt;k6/experimental/redis&lt;/code&gt; to &lt;code&gt;k6/x/redis&lt;/code&gt;. With &lt;a href=&#34;/docs/k6/latest/using-k6/auto-extension-resolution/&#34;&gt;auto-extension-resolution&lt;/a&gt;, k6 will automatically provision the &lt;a href=&#34;https://github.com/grafana/xk6-redis&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;xk6-redis&lt;/a&gt; extension when it sees the &lt;code&gt;k6/x/redis&lt;/code&gt; import.&lt;/p&gt;
&lt;h3 id=&#34;removed-exportertype-option-from-opentelemetry-output-5754&#34;&gt;Removed &lt;code&gt;ExporterType&lt;/code&gt; option from OpenTelemetry output &lt;a href=&#34;https://github.com/grafana/k6/pull/5754&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5754&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The deprecated &lt;code&gt;exporterType&lt;/code&gt; configuration option for the OpenTelemetry output has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Replace &lt;code&gt;K6_OTEL_EXPORTER_TYPE&lt;/code&gt; with &lt;code&gt;K6_OTEL_EXPORTER_PROTOCOL&lt;/code&gt;. The accepted values are &lt;code&gt;grpc&lt;/code&gt; and &lt;code&gt;http/protobuf&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;removed-singlecounterforrate-option-from-opentelemetry-output-5830&#34;&gt;Removed &lt;code&gt;SingleCounterForRate&lt;/code&gt; option from OpenTelemetry output &lt;a href=&#34;https://github.com/grafana/k6/pull/5830&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5830&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The temporary &lt;code&gt;SingleCounterForRate&lt;/code&gt; escape-hatch option for the OpenTelemetry output has been removed. It was introduced as a one-release migration aid in &lt;a href=&#34;https://github.com/grafana/k6/pull/5164&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5164&lt;/a&gt; to let users revert to the old pair-of-counters format (&lt;code&gt;&amp;lt;metric&amp;gt;.occurred&lt;/code&gt; &#43; &lt;code&gt;&amp;lt;metric&amp;gt;.total&lt;/code&gt;) while upgrading. Rate metrics are now always exported as a single counter with a &lt;code&gt;condition&lt;/code&gt; attribute (&lt;code&gt;nonzero&lt;/code&gt;/&lt;code&gt;zero&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Remove any &lt;code&gt;K6_OTEL_SINGLE_COUNTER_FOR_RATE=true&lt;/code&gt; configuration. If you were using the old pair-of-counters format, update your dashboards and queries to use the single counter with &lt;code&gt;condition&lt;/code&gt; attribute instead.&lt;/p&gt;
&lt;h3 id=&#34;removed-k6_binary_provisioning-environment-variable-5734&#34;&gt;Removed &lt;code&gt;K6_BINARY_PROVISIONING&lt;/code&gt; environment variable &lt;a href=&#34;https://github.com/grafana/k6/pull/5734&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5734&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;K6_BINARY_PROVISIONING&lt;/code&gt; environment variable, deprecated in v1.2.0, has been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Remove &lt;code&gt;K6_BINARY_PROVISIONING&lt;/code&gt; from your environment. Auto-extension-resolution is enabled by default; &lt;code&gt;K6_AUTO_EXTENSION_RESOLUTION&lt;/code&gt; only needs to be set explicitly if you want to disable it.&lt;/p&gt;
&lt;h3 id=&#34;removed-k6_enable_community_extensions-environment-variable-5733&#34;&gt;Removed &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; environment variable &lt;a href=&#34;https://github.com/grafana/k6/pull/5733&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5733&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; environment variable has been removed. The community and cloud extension catalogs were merged server-side, making this flag a no-op since the catalogs were unified.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Remove &lt;code&gt;K6_ENABLE_COMMUNITY_EXTENSIONS&lt;/code&gt; from your environment. Community extensions are now resolved through the default build service URL automatically.&lt;/p&gt;
&lt;h3 id=&#34;stack-is-now-required-for-all-k6-cloud-commands-5833&#34;&gt;Stack is now required for all &lt;code&gt;k6 cloud&lt;/code&gt; commands &lt;a href=&#34;https://github.com/grafana/k6/pull/5833&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5833&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Providing a stack is now mandatory for all &lt;code&gt;k6 cloud&lt;/code&gt; commands (&lt;code&gt;k6 cloud run&lt;/code&gt;, &lt;code&gt;k6 cloud upload&lt;/code&gt;, &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;). Previously, omitting a stack would fall back to the first available stack with a deprecation warning — that fallback has been removed. Likewise, &lt;code&gt;k6 cloud login&lt;/code&gt; now requires both a token and a stack; passing one without the other fails with an explicit error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Run &lt;code&gt;k6 cloud login&lt;/code&gt; which will ask you for the stack and setup correctly for the new version of k6. Alternatively, the &lt;code&gt;K6_CLOUD_STACK_ID&lt;/code&gt; environment variable, or the &lt;code&gt;stackID&lt;/code&gt; script option are available to be set before running any &lt;code&gt;k6 cloud&lt;/code&gt; command .&lt;/p&gt;
&lt;h3 id=&#34;removed-legacy-configuration-file-path-migration-5609&#34;&gt;Removed legacy configuration file path migration &lt;a href=&#34;https://github.com/grafana/k6/pull/5609&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5609&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 no longer automatically migrates configuration files from the old &lt;code&gt;{USER_CONFIG_DIR}/loadimpact/config.json&lt;/code&gt; path introduced before k6 v1.0.0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; If you still have a config file at the old path, move it to &lt;code&gt;{USER_CONFIG_DIR}/k6/config.json&lt;/code&gt;. You can also re-run &lt;code&gt;k6 cloud login&lt;/code&gt; to regenerate the file at the correct location.&lt;/p&gt;
&lt;h3 id=&#34;cloud-run-non-threshold-aborts-now-exit-with-code-97-5769&#34;&gt;Cloud run non-threshold aborts now exit with code 97 &lt;a href=&#34;https://github.com/grafana/k6/pull/5769&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5769&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Previously, most cloud abort scenarios returned an exit code of &lt;code&gt;0&lt;/code&gt;, making it impossible for CI pipelines to distinguish a failed run from a successful one. These scenarios now return exit code &lt;code&gt;97&lt;/code&gt;.&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Scenario&lt;/th&gt;
              &lt;th&gt;Before&lt;/th&gt;
              &lt;th&gt;After&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Finished&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Aborted by threshold&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;99&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;99&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Aborted by system, limit, script error, user, or timeout&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;97&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Review any CI pipeline logic that treats exit code &lt;code&gt;0&lt;/code&gt; as success for cloud runs. Cloud runs that abort for reasons other than threshold violations will now return exit code &lt;code&gt;97&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;browser-fid-metric-removed-web-vitals-updated-to-v510-5661&#34;&gt;Browser: FID metric removed, web vitals updated to v5.1.0 &lt;a href=&#34;https://github.com/grafana/k6/pull/5661&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5661&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;browser_web_vital_fid&lt;/code&gt; metric has been removed. FID (First Input Delay) was deprecated as a Core Web Vital and replaced by INP (Interaction to Next Paint). The embedded web-vitals library has been updated from v3 to v5.1.0, which reflects this change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Update any dashboards, alerts, or threshold rules that reference &lt;code&gt;browser_web_vital_fid&lt;/code&gt; to use &lt;code&gt;browser_web_vital_inp&lt;/code&gt; instead.&lt;/p&gt;
&lt;h3 id=&#34;dropped-easyjson-in-favor-of-stdlib-encodingjson-new-since-v200-rc1-5853&#34;&gt;Dropped easyjson in favor of stdlib encoding/json &lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5853&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5853&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 no longer uses &lt;code&gt;easyjson&lt;/code&gt; for JSON serialization. Several types in the public Go API previously had easyjson-generated &lt;code&gt;MarshalJSON&lt;/code&gt;/&lt;code&gt;UnmarshalJSON&lt;/code&gt; methods; those are now gone, replaced by stdlib &lt;code&gt;encoding/json&lt;/code&gt;. This change was intentionally held until v2 to avoid a breaking change mid-cycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; Extension authors who relied on easyjson-generated methods on k6 types should remove those dependencies. Standard &lt;code&gt;encoding/json&lt;/code&gt; marshaling applies to all affected types.&lt;/p&gt;
&lt;h3 id=&#34;http-api-server-no-longer-starts-by-default-new-since-v200-rc1-5876&#34;&gt;HTTP API server no longer starts by default &lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5876&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5876&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The k6 HTTP API server no longer starts automatically. Previously, k6 always listened on &lt;code&gt;localhost:6565&lt;/code&gt; unless opted out. The server now only starts when an address is explicitly provided via the &lt;code&gt;--address&lt;/code&gt; flag or the &lt;code&gt;K6_ADDRESS&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration:&lt;/strong&gt; If you use the k6 REST API, pass &lt;code&gt;--address=localhost:6565&lt;/code&gt; (or any address) to re-enable the server.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;cloud-secret-source-5738&#34;&gt;Cloud secret source &lt;a href=&#34;https://github.com/grafana/k6/pull/5738&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5738&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new built-in secret source, &lt;code&gt;cloud&lt;/code&gt;, allows k6 scripts to retrieve secrets stored in Grafana Cloud directly during local execution. Pass &lt;code&gt;--secret-source=cloud&lt;/code&gt; when running &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; to make cloud-managed secrets available to your script via &lt;code&gt;secrets.get()&lt;/code&gt;. Thanks, @vortegatorres!&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud run --local-execution --secret-source=cloud script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;cloud-secret-source-enabled-by-default-for-local-execution-new-since-v200-rc1-5875&#34;&gt;Cloud secret source enabled by default for local execution &lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5875&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5875&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;--secret-source=cloud&lt;/code&gt; is no longer required. When running &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt;, the cloud secret source is now automatically available and &lt;code&gt;secrets.get()&lt;/code&gt; works out of the box.&lt;/p&gt;
&lt;p&gt;To opt out, pass &lt;code&gt;--no-cloud-secrets&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud run --local-execution --no-cloud-secrets script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;k6-cloud-project-list-command-new-since-v200-rc1-5650&#34;&gt;&lt;code&gt;k6 cloud project list&lt;/code&gt; command &lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5650&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5650&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new &lt;code&gt;k6 cloud project&lt;/code&gt; command group has been added, with a &lt;code&gt;k6 cloud project list&lt;/code&gt; subcommand for listing Grafana Cloud k6 projects. The output supports both human-readable table format and JSON (&lt;code&gt;--format=json&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud project list
k6 cloud project list --format=json&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;extension-tab-completion-provisioning-5761&#34;&gt;Extension tab-completion provisioning &lt;a href=&#34;https://github.com/grafana/k6/pull/5761&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5761&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When using shell tab-completion, pressing TAB after a fully-typed extension name (e.g. &lt;code&gt;k6 x docs &amp;lt;TAB&amp;gt;&lt;/code&gt;) will now automatically provision a custom binary with that extension and delegate the completion request to it. This means extension-specific flags and subcommands are available via tab-completion without any manual setup.&lt;/p&gt;
&lt;h3 id=&#34;pre-manifest-extension-dependencies-captured-in-archive-metadata-5819&#34;&gt;Pre-manifest extension dependencies captured in archive metadata &lt;a href=&#34;https://github.com/grafana/k6/pull/5819&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5819&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When creating a k6 archive (&lt;code&gt;k6 archive&lt;/code&gt;), extension dependency information is now captured in &lt;code&gt;metadata.json&lt;/code&gt; under a &lt;code&gt;&amp;quot;dependencies&amp;quot;&lt;/code&gt; field, using the constraints declared by the script before any external manifest overrides are applied. This ensures that &lt;code&gt;k6/x/&lt;/code&gt; imports are preserved correctly during &lt;a href=&#34;/docs/k6/latest/using-k6/auto-extension-resolution/&#34;&gt;auto-extension-resolution&lt;/a&gt; re-execution.&lt;/p&gt;
&lt;h2 id=&#34;ux-improvements-and-enhancements&#34;&gt;UX improvements and enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5815&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5815&lt;/a&gt; Warns when &lt;code&gt;http.get()&lt;/code&gt; or &lt;code&gt;http.head()&lt;/code&gt; receive extra arguments that are silently ignored, helping catch common scripting mistakes. Thanks, @moko-poi!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5657&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5657&lt;/a&gt; Adds automatic retries to &lt;code&gt;newAction&lt;/code&gt;-based Locator APIs in the browser module, improving reliability of browser tests. Thanks, @janHildebrandt98!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5779&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5779&lt;/a&gt; Adds a &lt;code&gt;k6 x docs&lt;/code&gt; hint to the main k6 help output for built-in documentation discovery. Thanks, @Reranko05!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5788&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5788&lt;/a&gt; Adds a &lt;code&gt;k6 x explore&lt;/code&gt; hint to the main k6 help output for extension discovery.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5869&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5869&lt;/a&gt; Improves cloud authentication error messages — instead of a single generic error, k6 now reports specifically whether a token or a stack ID is missing.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5845&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5845&lt;/a&gt; Wires k6provider&amp;rsquo;s structured logging into k6&amp;rsquo;s logger. Provisioning operations (artifact resolution, cache hits, downloads, retries, and cache pruning) now appear in k6&amp;rsquo;s log output at the correct level.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; Exposes the host k6 version to provisioned subcommands via &lt;code&gt;K6_PROVISION_HOST_VERSION&lt;/code&gt;, so extension subcommands can correctly identify the caller&amp;rsquo;s k6 version when showing docs or feature information.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; Tab completion no longer blocks on a build when the requested extension binary is not in the local cache — completions bail out instantly on a cache miss instead of waiting 10–30 seconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5855&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5855&lt;/a&gt; Fixes auto-extension-resolution incorrectly triggering binary provisioning when a script manifest specifies a dependency as a Go module pseudo-version (&lt;code&gt;v0.0.0&#43;sha&lt;/code&gt; or &lt;code&gt;v0.0.0-timestamp-sha&lt;/code&gt;), even when the running binary already satisfied the constraint.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5769&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5769&lt;/a&gt; Fixes several &lt;code&gt;k6 cloud run&lt;/code&gt; TUI issues: ghost duplicate progress bars appearing after the run finishes, a timer that stalled and then jumped to 100%, and a stale timer racing with the status line.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5814&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5814&lt;/a&gt; Fixes &lt;code&gt;k6 cloud run --local-execution&lt;/code&gt; ignoring &lt;code&gt;K6_CLOUD_PUSH_REF_ID&lt;/code&gt; and unconditionally creating a new test run instead of reusing the provided run ID. Thanks, @Reranko05!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5716&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5716&lt;/a&gt; Fixes WebSocket &lt;code&gt;bufferedAmount&lt;/code&gt; not being incremented when sending TypedArrays, causing it to go negative. Thanks, @prakharbirla-ng!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5630&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5630&lt;/a&gt; Fixes duplicate redirect request metric emissions in the browser module, where each redirect was incorrectly emitting metrics for all prior redirects in the chain.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5786&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5786&lt;/a&gt; Fixes swapped Min and Max values for Gauge metrics in Cloud output v2, which caused incorrect peak and floor values in cloud test result queries. Thanks, @esquonk!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5785&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5785&lt;/a&gt; Fixes a race condition in the browser module&amp;rsquo;s &lt;code&gt;handleExitEvent&lt;/code&gt; where &lt;code&gt;Done()&lt;/code&gt; was signalled before the subscription was removed, causing tests to hang until the timeout.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5574&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5574&lt;/a&gt; Fixes position parser for base pointer options. Thanks, @chrismooreproductions!&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5905&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5905&lt;/a&gt; Fixes a deadlock where WebSocket connections hang forever during teardown when the server sends pings. A server ping arriving during shutdown could permanently stall the k6 process.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5923&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5923&lt;/a&gt; Fixes a nil pointer panic in &lt;code&gt;ElementHandle.DefaultTimeout&lt;/code&gt; (and any method that calls it, such as &lt;code&gt;GetAttribute&lt;/code&gt;) when invoked on a nil or partially-initialized handle. Thanks, @SAY-5!&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5620&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5620&lt;/a&gt; Preserves context cancellation causes across the scheduler, browser, and secret-source layers, improving the accuracy of error messages surfaced when a test is interrupted. Thanks, @LBaronceli!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-internal-improvements&#34;&gt;Maintenance and internal improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5847&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5847&lt;/a&gt; Moves &lt;code&gt;TaskQueue&lt;/code&gt; from the external &lt;code&gt;github.com/mstoykov/k6-taskqueue-lib&lt;/code&gt; into &lt;code&gt;internal/js/taskqueue&lt;/code&gt;, eliminating an awkward reverse dependency where the library&amp;rsquo;s own tests depended on k6.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5775&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5775&lt;/a&gt; Moves xk6-dashboard source code into &lt;code&gt;internal/dashboard&lt;/code&gt;, making the web dashboard a built-in part of the k6 binary without requiring a separate extension. The dashboard is available via &lt;code&gt;k6 run --out=web-dashboard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5703&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5703&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5818&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5818&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5817&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5817&lt;/a&gt; Uses Go&amp;rsquo;s &lt;code&gt;synctest&lt;/code&gt; package within &lt;code&gt;lib/executor&lt;/code&gt;, eventloop, timer, &lt;code&gt;PeriodicFlusher&lt;/code&gt;, &lt;code&gt;output/cloud&lt;/code&gt;, and &lt;code&gt;output/cloud/expv2&lt;/code&gt; tests for virtualized time, improving test determinism and CI speed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5778&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5778&lt;/a&gt; Bumps Ubuntu CI runners to 24.04 and always installs Chromium for browser tests.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5826&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5826&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5827&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5827&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel/exporters/otlp/otlptracehttp&lt;/code&gt; and &lt;code&gt;otlpmetrichttp&lt;/code&gt; to &lt;code&gt;v1.43.0&lt;/code&gt; [security], adding a 4 MiB response body cap to mitigate memory exhaustion from misconfigured or malicious servers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5669&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5669&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5911&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5911&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel&lt;/code&gt; packages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5740&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5740&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5796&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5796&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5910&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5910&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/sobek&lt;/code&gt; digest.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5783&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5783&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5838&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5838&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5862&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5862&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/k6-cloud-openapi-client-go&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5616&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5616&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5805&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5805&lt;/a&gt; Updates &lt;code&gt;github.com/evanw/esbuild&lt;/code&gt; to &lt;code&gt;v0.28.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5667&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5667&lt;/a&gt; Updates golangci-lint.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5799&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5799&lt;/a&gt; Updates &lt;code&gt;buf.build/gen/go/prometheus&lt;/code&gt; protobuf.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5800&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5800&lt;/a&gt; Updates &lt;code&gt;github.com/andybalholm/brotli&lt;/code&gt; to &lt;code&gt;v1.2.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5804&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5804&lt;/a&gt; Updates &lt;code&gt;examples/&lt;/code&gt; dependencies.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5806&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5806&lt;/a&gt; Updates &lt;code&gt;github.com/fatih/color&lt;/code&gt; to &lt;code&gt;v1.19.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5807&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5807&lt;/a&gt; Updates &lt;code&gt;github.com/puerkitobio/goquery&lt;/code&gt; to &lt;code&gt;v1.12.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5840&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5840&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5931&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5931&lt;/a&gt; Updates &lt;code&gt;github.com/mattn/go-isatty&lt;/code&gt; to &lt;code&gt;v0.0.22&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5857&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5857&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5950&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5950&lt;/a&gt; Updates Go to &lt;code&gt;v1.25.10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5863&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5863&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5880&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5880&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5908&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5908&lt;/a&gt; Updates &lt;code&gt;github.com/grafana/k6provider&lt;/code&gt; to &lt;code&gt;v0.5.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5670&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5670&lt;/a&gt; Updates GitHub artifact actions (major).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5693&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5693&lt;/a&gt; Updates &lt;code&gt;crazy-max/ghaction-chocolatey&lt;/code&gt; to &lt;code&gt;v4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5706&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5706&lt;/a&gt; Updates &lt;code&gt;actions/setup-go&lt;/code&gt; to &lt;code&gt;4a36011&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5707&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5707&lt;/a&gt; Updates &lt;code&gt;anchore/sbom-action&lt;/code&gt; to &lt;code&gt;v0.24.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5718&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5718&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5861&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5861&lt;/a&gt; Updates &lt;code&gt;github/codeql-action&lt;/code&gt; to &lt;code&gt;v4.35.2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5720&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5720&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5802&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5802&lt;/a&gt; Updates &lt;code&gt;docker/login-action&lt;/code&gt; to &lt;code&gt;v4.1.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5771&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5771&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5798&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5798&lt;/a&gt; Updates &lt;code&gt;grafana/shared-workflows&lt;/code&gt; actions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5836&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5836&lt;/a&gt; Updates &lt;code&gt;actions/upload-artifact&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5809&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5809&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5839&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5839&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/5951&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5951&lt;/a&gt; Updates the Docker base image tags (Debian to v13, Go to &lt;code&gt;v1.26.3&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5872&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5872&lt;/a&gt; Fixes v6 cloud client losing the request body when retrying after a &lt;code&gt;Connection: close&lt;/code&gt; response.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5871&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5871&lt;/a&gt; Returns an accurate error from the v6 cloud client when a test name lookup returns no results.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5751&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5751&lt;/a&gt; Updates &lt;code&gt;github.com/klauspost/compress&lt;/code&gt; to &lt;code&gt;v1.18.5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5841&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5841&lt;/a&gt; Updates &lt;code&gt;golang.org/x&lt;/code&gt; packages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5860&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5860&lt;/a&gt; Updates Alpine Docker image to &lt;code&gt;v3.23.4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5864&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5864&lt;/a&gt; Configures the v1 release branch to receive security updates via Renovate.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5891&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5891&lt;/a&gt; Sets &lt;code&gt;K6ModPath&lt;/code&gt; to the v2 module path in the k6provider config so that k6 v2 correctly signals to k6build which module to resolve against (&lt;code&gt;go.k6.io/k6/v2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5947&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5947&lt;/a&gt; Reworks the release workflow so pre-releases and older-major maintenance releases no longer claim Docker &lt;code&gt;:latest&lt;/code&gt; or the GitHub &amp;ldquo;Latest release&amp;rdquo; marker. Adds a floating &lt;code&gt;:vN&lt;/code&gt; Docker tag (e.g. &lt;code&gt;grafana/k6:v1&lt;/code&gt;) for users who want to pin to a major line.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5945&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5945&lt;/a&gt; Updates &lt;code&gt;golang.org/x/net&lt;/code&gt; to &lt;code&gt;v0.53.0&lt;/code&gt; [security] (CVE-2026-33814).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5932&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5932&lt;/a&gt; Updates &lt;code&gt;github.com/Masterminds/semver/v3&lt;/code&gt; to &lt;code&gt;v3.5.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5966&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5966&lt;/a&gt; Bumps &lt;code&gt;github.com/Azure/go-ntlmssp&lt;/code&gt; to &lt;code&gt;v0.1.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(new since v2.0.0-rc1)&lt;/em&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5965&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5965&lt;/a&gt; Bumps the Go version used by the xk6 test workflows to appease security scanners.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;external-contributors&#34;&gt;External contributors&lt;/h2&gt;
&lt;p&gt;A huge thank you to the external contributors who helped during this release: @moko-poi, @janHildebrandt98, @Reranko05, @prakharbirla-ng, @esquonk, @chrismooreproductions, @vortegatorres, @SAY-5, and @LBaronceli! 🙏&lt;/p&gt;
]]></content><description>&lt;p>k6 &lt;code>v2.0.0&lt;/code> is here 🎉!&lt;/p>
&lt;p>k6 v2.0.0 is the final release of the v2 major version, completing the cleanup of deprecated APIs, old commands, and obsolete configuration options that was started with v2.0.0-rc1. If you were already running the release candidate, this release includes a handful of additional changes on top — they are marked with &lt;strong>&lt;em>(new since v2.0.0-rc1)&lt;/em>&lt;/strong> throughout these notes.&lt;/p></description></item><item><title/><link>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.1.0/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/release-notes/v2.1.0/</guid><content><![CDATA[&lt;p&gt;k6 &lt;code&gt;v2.1.0&lt;/code&gt; is here 🎉! This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An opt-in feature-flag system — &lt;code&gt;--features&lt;/code&gt;, the &lt;code&gt;K6_FEATURES&lt;/code&gt; environment variable, and a &lt;code&gt;k6 features&lt;/code&gt; discovery command — shipping with experimental native histograms for trend metrics as its first flag.&lt;/li&gt;
&lt;li&gt;A context-level &lt;code&gt;proxy&lt;/code&gt; option for browser contexts.&lt;/li&gt;
&lt;li&gt;Subcommand discovery in &lt;code&gt;k6 x&lt;/code&gt;, so binaries can report which extension commands they expose.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;breaking-changes&#34;&gt;Breaking changes&lt;/h2&gt;
&lt;p&gt;There are no breaking changes in this release.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New features&lt;/h2&gt;
&lt;h3 id=&#34;k6-cloud-test-list-command-6007&#34;&gt;&lt;code&gt;k6 cloud test list&lt;/code&gt; command &lt;a href=&#34;https://github.com/grafana/k6/pull/6007&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6007&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new &lt;code&gt;k6 cloud test&lt;/code&gt; command group has been added, with a &lt;code&gt;k6 cloud test list&lt;/code&gt; subcommand that lists the load tests of a Grafana Cloud k6 project. It complements the &lt;code&gt;k6 cloud project list&lt;/code&gt; command introduced in v2.0.0.&lt;/p&gt;
&lt;p&gt;The project to list tests for is resolved in the following order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;--project-id&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;K6_CLOUD_PROJECT_ID&lt;/code&gt; environment variable (cloud config &lt;code&gt;projectID&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The default project of the configured stack, populated by &lt;code&gt;k6 cloud login&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Output defaults to a human-readable table. Pass &lt;code&gt;--json&lt;/code&gt; to emit a JSON array instead, mirroring the format established by &lt;code&gt;k6 cloud project list&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 cloud test list
k6 cloud test list --project-id 12345
k6 cloud test list --json&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;feature-flags-and-experimental-native-histograms-6055-6056&#34;&gt;Feature flags and experimental native histograms &lt;a href=&#34;https://github.com/grafana/k6/pull/6055&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6055&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6056&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6056&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;k6 now has an opt-in feature-flag mechanism for trialing new, not-yet-stable behavior without affecting existing runs. Flags can be enabled on &lt;code&gt;k6 run&lt;/code&gt; and &lt;code&gt;k6 cloud run&lt;/code&gt; through the &lt;code&gt;--features&lt;/code&gt; flag (comma-separated or repeated), the &lt;code&gt;K6_FEATURES&lt;/code&gt; environment variable, or the &lt;code&gt;features&lt;/code&gt; key in &lt;code&gt;config.json&lt;/code&gt;. Enabled flags are surfaced as metric tags and propagated into archives and cloud workers so a run behaves consistently wherever it executes.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;k6 features&lt;/code&gt; (or &lt;code&gt;k6 features --json&lt;/code&gt;) to discover the available flags and their lifecycle:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;console&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-console&#34;&gt;$ k6 features
FEATURE             LIFECYCLE      DESCRIPTION
native-histograms   Experimental   Use native histograms for trend metrics&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The first flag shipped is &lt;code&gt;native-histograms&lt;/code&gt;, an experimental flag that makes k6 use native histograms for trend metrics:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;k6 run --features native-histograms script.js
# or
K6_FEATURES=native-histograms k6 run script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;subcommand-discovery-in-k6-x-5972&#34;&gt;Subcommand discovery in &lt;code&gt;k6 x&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/5972&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5972&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Running &lt;code&gt;k6 x&lt;/code&gt; now lists the available subcommands — both the ones baked into the binary and those advertised by the extension registry (official and community). Tab-completion surfaces the same set once the catalog has been cached locally by a prior &lt;code&gt;k6 x&lt;/code&gt; run, so completion never blocks on the network.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;text&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;$ k6 x
...
Available Commands:
  agent       Bootstrap an AI-assisted k6 testing workflow in any editor
  docs        CLI k6 docs for AI agents and users
  explore     Explore k6 extensions for Automatic Resolution
  mcp         An MCP server for k6 for AI agents&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This makes a k6 binary self-describing — particularly useful for AI agents driving k6, which previously had no way to introspect which extension subcommands were available.&lt;/p&gt;
&lt;h3 id=&#34;browser-context-proxy-option-5924&#34;&gt;Browser context proxy option &lt;a href=&#34;https://github.com/grafana/k6/pull/5924&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5924&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Browser contexts can now be configured with a context-level &lt;code&gt;proxy&lt;/code&gt; option, letting you route a context&amp;rsquo;s traffic through a proxy without launching a custom-built binary or relying on environment proxy variables (which only affected k6&amp;rsquo;s DevTools WebSocket connection). The option is wired to Chromium through &lt;code&gt;Target.createBrowserContext&lt;/code&gt;, and invalid proxy configuration now fails early when &lt;code&gt;proxy.server&lt;/code&gt; is missing. Thanks, @nightt5879!&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;const context = await browser.newContext({
  proxy: {
    server: &amp;#34;http://proxy.test:8080&amp;#34;,
    bypass: &amp;#34;localhost,127.0.0.1&amp;#34;,
  },
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;browser-locatorisinviewport-6023&#34;&gt;Browser &lt;code&gt;locator.isInViewport()&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/6023&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6023&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new &lt;code&gt;locator.isInViewport()&lt;/code&gt; method reports whether an element intersects the browser viewport. It accepts an optional &lt;code&gt;ratio&lt;/code&gt; (0 to 1) that sets how much of the element must be visible, defaulting to &lt;code&gt;0&lt;/code&gt; so any visible pixel counts, matching Playwright&amp;rsquo;s &lt;code&gt;toBeInViewport&lt;/code&gt; semantics. The call waits for the element to attach, honoring the &lt;code&gt;timeout&lt;/code&gt; option, then measures the intersection once. Thanks, @Anuragp22!&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;const button = page.locator(&amp;#34;button#submit&amp;#34;);
if (await button.isInViewport()) {
  await button.click();
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;basic-auth-for-the-opentelemetry-http-exporter-5997&#34;&gt;Basic auth for the OpenTelemetry HTTP exporter &lt;a href=&#34;https://github.com/grafana/k6/pull/5997&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5997&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The OpenTelemetry output&amp;rsquo;s HTTP exporter can now send HTTP Basic Auth credentials. Set them through the &lt;code&gt;K6_OTEL_HTTP_EXPORTER_USERNAME&lt;/code&gt; and &lt;code&gt;K6_OTEL_HTTP_EXPORTER_PASSWORD&lt;/code&gt; environment variables, or the &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; keys in the output config.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;shell&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;K6_OTEL_HTTP_EXPORTER_USERNAME=user \
K6_OTEL_HTTP_EXPORTER_PASSWORD=secret \
k6 run --out opentelemetry script.js&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;single-selection-helper-in-k6html-6002&#34;&gt;&lt;code&gt;single()&lt;/code&gt; selection helper in &lt;code&gt;k6/html&lt;/code&gt; &lt;a href=&#34;https://github.com/grafana/k6/pull/6002&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6002&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Selection.single(selector)&lt;/code&gt; returns at most one matching element, backed by goquery&amp;rsquo;s &lt;code&gt;Single&lt;/code&gt; matcher for a faster lookup than &lt;code&gt;find()&lt;/code&gt; when you only need the first match. Thanks, @rohan-patnaik!&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;js&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-js&#34;&gt;import { parseHTML } from &amp;#34;k6/html&amp;#34;;

const doc = parseHTML(content);
const title = doc.single(&amp;#34;h1&amp;#34;).text();&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;ux-improvements-and-enhancements&#34;&gt;UX improvements and enhancements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5971&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5971&lt;/a&gt; Tags browser API failures with &lt;code&gt;module=browser&lt;/code&gt; so that browser errors surfaced in Grafana Cloud Logs can be filtered separately from other log sources.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5794&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5794&lt;/a&gt; Makes the &lt;code&gt;--vus&lt;/code&gt; flag work as a standalone execution shortcut instead of being silently ignored when a script defines scenarios. Running &lt;code&gt;k6 run script.js --vus N&lt;/code&gt; now creates a &lt;code&gt;shared-iterations&lt;/code&gt; scenario with &lt;code&gt;N&lt;/code&gt; VUs and &lt;code&gt;N&lt;/code&gt; iterations, overriding any script-defined scenarios with a warning — consistent with how &lt;code&gt;--iterations&lt;/code&gt;, &lt;code&gt;--duration&lt;/code&gt;, and &lt;code&gt;--stages&lt;/code&gt; already behave. Thanks, @Reranko05!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6013&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6013&lt;/a&gt; Rejects invalid threshold percentiles. A percentile aggregation value outside the 0 to 100 range (or &lt;code&gt;NaN&lt;/code&gt;) now fails parsing with a clear message instead of being silently accepted. Thanks, @immanuwell!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6011&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6011&lt;/a&gt; Writes the on-disk k6 config file with owner-only permissions (&lt;code&gt;0o600&lt;/code&gt;, inside a &lt;code&gt;0o700&lt;/code&gt; directory). The file can hold the Grafana Cloud API token (&lt;code&gt;collectors.cloud.token&lt;/code&gt;), so tightening it keeps other local users on shared hosts (CI runners, multi-user boxes, sidecar containers) from reading the token. Existing configs are upgraded on the next write, for example the next &lt;code&gt;k6 cloud login&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;maintenance-and-internal-improvements&#34;&gt;Maintenance and internal improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6033&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6033&lt;/a&gt; Moves Docker Hub image publishing to a Google Artifact Registry mirror.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5953&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5953&lt;/a&gt; Installs &lt;code&gt;s3cmd&lt;/code&gt; via &lt;code&gt;apt&lt;/code&gt; instead of &lt;code&gt;pip&lt;/code&gt; to fix the &lt;code&gt;k6packager&lt;/code&gt; image build.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6052&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6052&lt;/a&gt; Fixes the browser end-to-end test workflow.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5984&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5984&lt;/a&gt; Routes per-test logger output to the test instance instead of the global &lt;code&gt;logrus&lt;/code&gt;, improving test isolation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5985&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5985&lt;/a&gt; Fixes the flaky &lt;code&gt;TestPageScreenshotFullpage&lt;/code&gt; browser test.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5981&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5981&lt;/a&gt; Lets Renovate track the Go minor version in the &lt;code&gt;setup-go&lt;/code&gt; workflows.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5968&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5968&lt;/a&gt; Honors the caller-pinned ref in the shared lint action.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6041&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6041&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6054&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6054&lt;/a&gt; Aligns the Go module directive and toolchain (1.25.0 / 1.25.11).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5967&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5967&lt;/a&gt; Updates the release notes template after v2.0.0.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6015&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6015&lt;/a&gt; Updates &lt;code&gt;golang.org/x/net&lt;/code&gt; to &lt;code&gt;v0.55.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6026&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6026&lt;/a&gt; Updates &lt;code&gt;golang.org/x/crypto&lt;/code&gt; to &lt;code&gt;v0.52.0&lt;/code&gt; [security].&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5962&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5962&lt;/a&gt;, &lt;a href=&#34;https://github.com/grafana/k6/pull/6028&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6028&lt;/a&gt; Updates &lt;code&gt;google.golang.org/grpc&lt;/code&gt; to &lt;code&gt;v1.81.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5960&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5960&lt;/a&gt; Updates &lt;code&gt;grafana/shared-workflows/get-vault-secrets&lt;/code&gt; to &lt;code&gt;v1.3.2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5958&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5958&lt;/a&gt; Updates &lt;code&gt;grafana/shared-workflows/azure-trusted-signing&lt;/code&gt; to &lt;code&gt;v1.0.2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5957&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5957&lt;/a&gt; Updates &lt;code&gt;github/codeql-action&lt;/code&gt; to &lt;code&gt;v4.35.4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5959&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5959&lt;/a&gt; Updates &lt;code&gt;grafana/shared-workflows/dockerhub-login&lt;/code&gt; to &lt;code&gt;v1.0.4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6009&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6009&lt;/a&gt; Moves the browser &lt;code&gt;PageScreenshotOptions&lt;/code&gt; parsing into the mapping layer.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/5964&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#5964&lt;/a&gt; Adds the k6 feature-flags specification under &lt;code&gt;openspec/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6067&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6067&lt;/a&gt; Forces the legacy &lt;code&gt;x/net/http2&lt;/code&gt; implementation on the &lt;code&gt;gotip&lt;/code&gt; CI test job.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6065&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6065&lt;/a&gt; Lets Renovate update the &lt;code&gt;Dockerfile&lt;/code&gt; on the v1.x branch.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6031&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6031&lt;/a&gt; Updates &lt;code&gt;go.opentelemetry.io/otel&lt;/code&gt; to &lt;code&gt;v1.44.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6086&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6086&lt;/a&gt; Updates &lt;code&gt;golang.org/x&lt;/code&gt; dependencies (&lt;code&gt;crypto&lt;/code&gt; to &lt;code&gt;v0.53.0&lt;/code&gt;, &lt;code&gt;net&lt;/code&gt; to &lt;code&gt;v0.56.0&lt;/code&gt;, &lt;code&gt;term&lt;/code&gt; to &lt;code&gt;v0.44.0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6061&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6061&lt;/a&gt; Updates &lt;code&gt;golang.org/x/sync&lt;/code&gt; to &lt;code&gt;v0.21.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6030&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6030&lt;/a&gt; Updates &lt;code&gt;github.com/tidwall/gjson&lt;/code&gt; to &lt;code&gt;v1.19.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6029&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6029&lt;/a&gt; Updates &lt;code&gt;github.com/mccutchen/go-httpbin/v2&lt;/code&gt; to &lt;code&gt;v2.23.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6063&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6063&lt;/a&gt; Updates &lt;code&gt;github.com/mattn/go-colorable&lt;/code&gt; to &lt;code&gt;v0.1.15&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6062&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6062&lt;/a&gt; Updates the Golang Docker image to &lt;code&gt;1.26.4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6084&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6084&lt;/a&gt; Updates the Alpine Docker image to &lt;code&gt;3.24.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/grafana/k6/pull/6027&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;#6027&lt;/a&gt; Updates the Debian Docker image to &lt;code&gt;trixie-20260518&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;external-contributors&#34;&gt;External contributors&lt;/h2&gt;
&lt;p&gt;A huge thank you to the external contributors who helped during this release: @nightt5879, @Reranko05, @rohan-patnaik, @immanuwell, and @Anuragp22! 🙏&lt;/p&gt;
]]></content><description>&lt;p>k6 &lt;code>v2.1.0&lt;/code> is here 🎉! This release includes:&lt;/p>
&lt;ul>
&lt;li>An opt-in feature-flag system — &lt;code>--features&lt;/code>, the &lt;code>K6_FEATURES&lt;/code> environment variable, and a &lt;code>k6 features&lt;/code> discovery command — shipping with experimental native histograms for trend metrics as its first flag.&lt;/li>
&lt;li>A context-level &lt;code>proxy&lt;/code> option for browser contexts.&lt;/li>
&lt;li>Subcommand discovery in &lt;code>k6 x&lt;/code>, so binaries can report which extension commands they expose.&lt;/li>
&lt;/ul>
&lt;h2 id="breaking-changes">Breaking changes&lt;/h2>
&lt;p>There are no breaking changes in this release.&lt;/p></description></item></channel></rss>