I’m using k6/x/browser and while running in headed mode my interactions with the browser has no issues.
However when I want to use headless mode (because I want to run multiple concurrent vus) then I run in to issues and that interactions with the browser doesn’t want to happen.
Any ideas on how I can run in headless mode using multiple concurrent vus?
There are 2 API calls I make which then provides a redirect URL which I then navigate to using xk6 browser.
On that page I enter some payment details and click on a submit button which redirect to a 3DS page where I enter a password and click on a submit button again which then redirects back and will either say payment successful or failed.
This all works pretty well even using 3 concurrent users while in headed mode. But when headless it doesn’t even interact with the first page. I’m more than happy to share my script if that will help.
I am using the latest xk6 browser.
I’m just not sure if I’m doing something wrong or if xk6 browser was never intended to be used headless with multiple VUs.
I actually want to stress test this flow so wanted to throw a lot more than 3 VUs at it hence wanting to use headless mode.
Below the code I use for the browser interactions.
I had some issues making sure I wait for the locators to be available so played around a lot with waitForNavigation / waitForLoadState, using promises etc.
I would appreciate it a lot for any assistance on how best to approach this, with the main aim to stress test this flow.
const browser = chromium.launch();
const context = browser.newContext();
const page = context.newPage();
console.log(`VU ${__VU} Redirect URL: ${redirectUrl}`)
page
.goto(redirectUrl, { waitUntil: 'networkidle', timeout: 10000, allowHttpErrors: true })
.then(() => {
const cvvInput = page.locator('input[name="cvc"]')
// cvvInput.waitFor({
// state: 'visible',
// });
console.log(`VU ${__VU} Enter CVV`)
cvvInput.type(cvv)
const submitInput = page.locator('button[type="submit"]')
// submitInput.waitFor({
// state: 'visible',
// });
return Promise.all([
console.log(`VU ${__VU} Click on Submit and Redirect to 3DS`),
page.waitForNavigation({waitUntil: 'load'}),
page.waitForLoadState('networkidle'),
submitInput.click(),
]).then(() => {
let pageContent = page.content()
if (pageContent.includes('Payment Failed')) {
console.log(`VU ${__VU} Transaction Status: Payment Failed Before 3DS Redirect`)
sleep(1)
//console.log(pageContent)
} else {
const passwordInput = page.locator('input[name="password"]')
// passwordInput.waitFor({
// state: 'visible',
// });
console.log(`VU ${__VU} Enter Password`)
passwordInput.type('test123')
const threedsSubmit = page.locator('//input[@value=\'Submit\']')
// threedsSubmit.waitFor({
// state: 'visible',
// });
return Promise.all([
console.log(`VU ${__VU} Click 3DS Submit`),
page.waitForNavigation({waitUntil: 'load'}),
page.waitForLoadState('networkidle'),
threedsSubmit.click(),
]).then(() => {
page.waitForNavigation({waitUntil: 'load'})
page.waitForLoadState('networkidle')
let pageContent = page.content()
if (pageContent.includes('Payment Successful')) {
console.log(`VU ${__VU} Transaction Status: Payment Successful`)
} else {
console.log(`VU ${__VU} Transaction Status: Payment Failed`)
}
// const redirect = page.locator('a[title="Redirect Now"]');
sleep(1)
});
}
});
}).finally(() => {
page.close();
browser.close();
const now = new Date();
let testEndTimestamp = new Date();
const durationInSeconds = (testEndTimestamp.getTime() - testStartTimestamp.getTime()) / 1000;
console.log(`VU ${__VU} Test Complete: ${testEndTimestamp.toLocaleDateString()} ${testEndTimestamp.toLocaleTimeString()} Duration: ${durationInSeconds}`);
});