Skip to content

Commit 8c788a4

Browse files
authored
Merge pull request #1430 from grafana/update-k6-browser-examples
docs: add a few more k6 browser examples
2 parents ddcef79 + d996398 commit 8c788a4

File tree

10 files changed

+542
-4
lines changed

10 files changed

+542
-4
lines changed

docs/sources/next/javascript-api/k6-experimental/browser/browsercontext/newpage.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Uses the `BrowserContext` to create a new [Page](https://grafana.com/docs/k6/<K6
2121
import { browser } from 'k6/experimental/browser';
2222

2323
export default async function () {
24-
const page = browser.newPage();
24+
const context = browser.newContext();
25+
const page = context.newPage();
2526

2627
try {
2728
await page.goto('https://test.k6.io/browser.php');

docs/sources/next/using-k6-browser/metrics.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,73 @@ When the test is run, you should see a similar output as the one below.
9898
✓ browser_web_vital_lcp..........................: avg=460.1ms min=460.1ms med=460.1ms max=460.1ms p(90)=460.1ms p(95)=460.1ms
9999
browser_web_vital_ttfb.........................: avg=339.3ms min=258.9ms med=339.3ms max=419.7ms p(90)=403.62ms p(95)=411.66ms
100100
```
101+
102+
## Measure custom metrics
103+
104+
When using the k6 browser `page.evaluate` function, you can call the [Performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API) to measure the performance of web applications. For example, if you want to measure the time it takes for your users to complete actions, such as a search feature, you can use the [`performance.mark`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark) method to add a timestamp in your browser's performance timeline.
105+
106+
Using the [`performance.measure`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure) method, you can also measure the time difference between two performance markers. The time duration that `performance.measure` returns can be added as a custom metric in k6 browser using [Trends](https://k6.io/docs/javascript-api/k6-metrics/trend/).
107+
108+
{{< code >}}
109+
110+
```javascript
111+
import { browser } from "k6/experimental/browser";
112+
import { Trend } from "k6/metrics";
113+
114+
export const options = {
115+
scenarios: {
116+
ui: {
117+
executor: "shared-iterations",
118+
options: {
119+
browser: {
120+
type: "chromium",
121+
},
122+
},
123+
},
124+
},
125+
};
126+
127+
const myTrend = new Trend('total_action_time');
128+
129+
export default async function () {
130+
const page = browser.newPage();
131+
132+
try {
133+
await page.goto('https://test.k6.io/browser.php');
134+
page.evaluate(() => window.performance.mark('page-visit'));
135+
136+
page.locator('#checkbox1').check();
137+
page.locator('#counter-button"]').click();
138+
page.locator('#text1').fill('This is a test');
139+
140+
page.evaluate(() => window.performance.mark('action-completed'));
141+
142+
// Get time difference between visiting the page and completing the actions
143+
page.evaluate(() =>
144+
window.performance.measure(
145+
'total-action-time',
146+
'page-visit',
147+
'action-completed',
148+
)
149+
);
150+
151+
const totalActionTime = page.evaluate(() =>
152+
JSON.parse(JSON.stringify(window.performance.getEntriesByName('total-action-time')))[0].duration
153+
);
154+
155+
myTrend.add(total_action_time);
156+
} finally {
157+
page.close();
158+
}
159+
}
160+
```
161+
162+
{{< /code >}}
163+
164+
After you run the test, you should see a similar output as the one below.
165+
166+
```bash
167+
iteration_duration..........: avg=1.06s min=1.06s med=1.06s max=1.06s p(90)=1.06s p(95)=1.06s
168+
iterations..................: 1 0.70866/s
169+
total_action_time.............: avg=295.3 min=295.3 med=295.3 max=295.3 p(90)=295.3 p(95)=295.3
170+
```

docs/sources/next/using-k6-browser/recommended-practices/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ weight: 100
99

1010
This section presents some examples and recommended practices when working with the `k6 browser` module to leverage browser automation as part of your k6 tests.
1111

12+
- [Hybrid approach to performance](https://grafana.com/docs/k6/<K6_VERSION>/using-k6-browser/recommended-practices/hybrid-approach-to-performance)
1213
- [Page object model pattern](https://grafana.com/docs/k6/<K6_VERSION>/using-k6-browser/recommended-practices/page-object-model-pattern)
1314
- [Selecting elements](https://grafana.com/docs/k6/<K6_VERSION>/using-k6-browser/recommended-practices/selecting-elements)
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: 'Hybrid approach to performance'
3+
heading: 'Hybrid performance with k6 browser'
4+
head_title: 'Hybrid performance with k6 browser'
5+
excerpt: 'An example on how to implement a hybrid approach to performance with k6 browser'
6+
weight: 01
7+
---
8+
9+
# Hybrid performance with k6 browser
10+
11+
An alternative approach to [browser-based load testing](https://grafana.com/docs/k6/<K6_VERSION>/testing-guides/load-testing-websites/#browser-based-load-testing) that's much less resource-intensive is combining a small number of virtual users for a browser test with a large number of virtual users for a protocol-level test.
12+
13+
You can achieve hybrid performance in multiple ways, often by using different tools. To simplify the developer experience, you can combine k6 browser with core k6 features to write hybrid tests in a single script.
14+
15+
## Browser and HTTP test
16+
17+
The code below shows an example of combining a browser and HTTP test in a single script. While the script exposes the backend to the typical load, it also checks the frontend for any unexpected issues. It also defines thresholds to check both HTTP and browser metrics against pre-defined SLOs.
18+
19+
{{< code >}}
20+
21+
```javascript
22+
import http from "k6/http";
23+
import { check } from "k6";
24+
import { browser } from "k6/experimental/browser";
25+
26+
const BASE_URL = __ENV.BASE_URL;
27+
28+
export const options = {
29+
scenarios: {
30+
load: {
31+
exec: 'getPizza',
32+
executor: 'ramping-vus',
33+
stages: [
34+
{ duration: '5s', target: 5 },
35+
{ duration: '10s', target: 5 },
36+
{ duration: '5s', target: 0 },
37+
],
38+
startTime: '10s',
39+
},
40+
browser: {
41+
exec: 'checkFrontend',
42+
executor: 'constant-vus',
43+
vus: 1,
44+
duration: '30s',
45+
options: {
46+
browser: {
47+
type: 'chromium',
48+
},
49+
},
50+
}
51+
},
52+
thresholds: {
53+
http_req_failed: ['rate<0.01'],
54+
http_req_duration: ['p(95)<500', 'p(99)<1000'],
55+
browser_web_vital_fcp: ["p(95) < 1000"],
56+
browser_web_vital_lcp: ["p(95) < 2000"],
57+
},
58+
};
59+
60+
export function getPizza() {
61+
let restrictions = {
62+
maxCaloriesPerSlice: 500,
63+
mustBeVegetarian: false,
64+
excludedIngredients: ['pepperoni'],
65+
excludedTools: ['knife'],
66+
maxNumberOfToppings: 6,
67+
minNumberOfToppings: 2
68+
}
69+
70+
let res = http.post(`${BASE_URL}/api/pizza`, JSON.stringify(restrictions), {
71+
headers: {
72+
'Content-Type': 'application/json',
73+
'X-User-ID': customers[Math.floor(Math.random() * customers.length)],
74+
},
75+
});
76+
77+
check(res, {
78+
'status is 200': (res) => res.status === 200
79+
});
80+
}
81+
82+
export async function checkFrontend() {
83+
const page = browser.newPage();
84+
85+
try {
86+
await page.goto(BASE_URL)
87+
check(page, {
88+
'header': page.locator('h1').textContent() == 'Looking to break out of your pizza routine?',
89+
});
90+
91+
await page.locator('//button[. = "Pizza, Please!"]').click();
92+
page.waitForTimeout(500);
93+
page.screenshot({ path: `screenshots/${__ITER}.png` });
94+
95+
check(page, {
96+
'recommendation': page.locator('div#recommendations').textContent() != '',
97+
});
98+
} finally {
99+
page.close();
100+
}
101+
}
102+
103+
```
104+
105+
{{< /code >}}
106+
107+
## Browser and failure injection test
108+
109+
You can also run a browser test together with a failure injection test by using the [xk6-disruptor](https://github.com/grafana/xk6-disruptor) extension. This approach lets you find issues in your front end if any services it depends on are suddenly injected with failures, such as delays or server errors.
110+
111+
The following code shows an example of how to introduce faults to a Kubernetes service. At the same time, the `browser` scenario runs to ensure the frontend application is free of any unexpected errors that may not have been handled properly.
112+
113+
To find out more information about injecting faults to your service, check out the [Get started with xk6-disruptor guide](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/xk6-disruptor/get-started/).
114+
115+
{{< code >}}
116+
117+
```javascript
118+
import http from "k6/http";
119+
import { check } from "k6";
120+
import { browser } from "k6/experimental/browser";
121+
122+
const BASE_URL = __ENV.BASE_URL;
123+
124+
export const options = {
125+
scenarios: {
126+
disrupt: {
127+
executor: "shared-iterations",
128+
iterations: 1,
129+
vus: 1,
130+
exec: "disrupt",
131+
},
132+
browser: {
133+
executor: "constant-vus",
134+
vus: 1,
135+
duration: "10s",
136+
startTime: "10s",
137+
exec: "browser",
138+
options: {
139+
browser: {
140+
type: "chromium",
141+
},
142+
},
143+
},
144+
},
145+
thresholds: {
146+
browser_web_vital_fcp: ["p(95) < 1000"],
147+
browser_web_vital_lcp: ["p(95) < 2000"],
148+
},
149+
};
150+
151+
// Add faults to the service by introducing a delay of 1s and 503 errors to 10% of the requests.
152+
const fault = {
153+
averageDelay: "1000ms",
154+
errorRate: 0.1,
155+
errorCode: 503,
156+
}
157+
158+
export function disrupt() {
159+
const disruptor = new ServiceDisruptor("pizza-info", "pizza-ns");
160+
const targets = disruptor.targets();
161+
if (targets.length == 0) {
162+
throw new Error("expected list to have one target");
163+
}
164+
165+
disruptor.injectHTTPFaults(fault, "20s");
166+
}
167+
168+
export async function checkFrontend() {
169+
const page = browser.newPage();
170+
171+
try {
172+
await page.goto(BASE_URL)
173+
check(page, {
174+
'header': page.locator('h1').textContent() == 'Looking to break out of your pizza routine?',
175+
});
176+
177+
await page.locator('//button[. = "Pizza, Please!"]').click();
178+
page.waitForTimeout(500);
179+
page.screenshot({ path: `screenshots/${__ITER}.png` });
180+
181+
check(page, {
182+
'recommendation': page.locator('div#recommendations').textContent() != '',
183+
});
184+
} finally {
185+
page.close();
186+
}
187+
}
188+
189+
```
190+
191+
{{< /code >}}
192+
193+
## Recommended practices
194+
195+
- **Start small**. Start with a small number of browser-based virtual users. A good starting point is to have 10% virtual users or less to monitor the user experience for your end-users, while the script emulates around 90% of traffic from the protocol level.
196+
- **Combine browser test with different load testing types**. To fully understand the impact of different traffic patterns on your end-user experience, experiment with running your browser test with different [load testing types](https://grafana.com/docs/k6/<K6_VERSION>/testing-guides/test-types/).
197+
- **Focus on high-risk user journeys as a start**. Identify the high-risk user journeys first so you can start monitoring the web performance metrics for them while your backend applications are being exposed to high traffic or service faults.

docs/sources/next/using-k6-browser/recommended-practices/page-object-model-pattern.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: 'Page object model'
33
heading: 'Page object model with k6 browser'
44
head_title: 'Page object model with k6 browser'
55
excerpt: 'An example on how to implement page object model design pattern with k6 browser'
6-
weight: 01
6+
weight: 02
77
---
88

99
# Page object model

docs/sources/next/using-k6-browser/recommended-practices/selecting-elements.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: 'Selecting elements'
33
excerpt: 'A guide on how to select elements with the browser module.'
4-
weight: 02
4+
weight: 03
55
---
66

77
# Selecting elements

docs/sources/v0.47.x/javascript-api/k6-experimental/browser/browsercontext/newpage.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Uses the `BrowserContext` to create a new [Page](https://grafana.com/docs/k6/<K6
2121
import { browser } from 'k6/experimental/browser';
2222

2323
export default async function () {
24-
const page = browser.newPage();
24+
const context = browser.newContext();
25+
const page = context.newPage();
2526

2627
try {
2728
await page.goto('https://test.k6.io/browser.php');

docs/sources/v0.47.x/using-k6-browser/metrics.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,73 @@ When the test is run, you should see a similar output as the one below.
9898
✓ browser_web_vital_lcp..........................: avg=460.1ms min=460.1ms med=460.1ms max=460.1ms p(90)=460.1ms p(95)=460.1ms
9999
browser_web_vital_ttfb.........................: avg=339.3ms min=258.9ms med=339.3ms max=419.7ms p(90)=403.62ms p(95)=411.66ms
100100
```
101+
102+
## Measure custom metrics
103+
104+
When using the k6 browser `page.evaluate` function, you can call the [Performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API) to measure the performance of web applications. For example, if you want to measure the time it takes for your users to complete actions, such as a search feature, you can use the [`performance.mark`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark) method to add a timestamp in your browser's performance timeline.
105+
106+
Using the [`performance.measure`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure) method, you can also measure the time difference between two performance markers. The time duration that `performance.measure` returns can be added as a custom metric in k6 browser using [Trends](https://k6.io/docs/javascript-api/k6-metrics/trend/).
107+
108+
{{< code >}}
109+
110+
```javascript
111+
import { browser } from "k6/experimental/browser";
112+
import { Trend } from "k6/metrics";
113+
114+
export const options = {
115+
scenarios: {
116+
ui: {
117+
executor: "shared-iterations",
118+
options: {
119+
browser: {
120+
type: "chromium",
121+
},
122+
},
123+
},
124+
},
125+
};
126+
127+
const myTrend = new Trend('total_action_time');
128+
129+
export default async function () {
130+
const page = browser.newPage();
131+
132+
try {
133+
await page.goto('https://test.k6.io/browser.php');
134+
page.evaluate(() => window.performance.mark('page-visit'));
135+
136+
page.locator('#checkbox1').check();
137+
page.locator('#counter-button"]').click();
138+
page.locator('#text1').fill('This is a test');
139+
140+
page.evaluate(() => window.performance.mark('action-completed'));
141+
142+
// Get time difference between visiting the page and completing the actions
143+
page.evaluate(() =>
144+
window.performance.measure(
145+
'total-action-time',
146+
'page-visit',
147+
'action-completed',
148+
)
149+
);
150+
151+
const totalActionTime = page.evaluate(() =>
152+
JSON.parse(JSON.stringify(window.performance.getEntriesByName('total-action-time')))[0].duration
153+
);
154+
155+
myTrend.add(total_action_time);
156+
} finally {
157+
page.close();
158+
}
159+
}
160+
```
161+
162+
{{< /code >}}
163+
164+
After you run the test, you should see a similar output as the one below.
165+
166+
```bash
167+
iteration_duration..........: avg=1.06s min=1.06s med=1.06s max=1.06s p(90)=1.06s p(95)=1.06s
168+
iterations..................: 1 0.70866/s
169+
total_action_time.............: avg=295.3 min=295.3 med=295.3 max=295.3 p(90)=295.3 p(95)=295.3
170+
```

0 commit comments

Comments
 (0)