How to keep the browser open?

Hey folks, nice to meet you.

I have written a simple test with K6 browser – it logs into a website and then brings up a sequential series of pages.

It seems to work, but no matter what I do the browser closes at 30 seconds, causing all remaining actions to fail. I’ve spent time searching on the web and in the documentation, and I haven’t figured out a way around it. Perhaps it is related to the gracefulStop parameter, but it complains about an unknown option when placed in options.

I’m sure there is something very silly that I’m overlooking, but I’ve been banging my head against this for several hours and have not made any progress.

Thanks in advance for any assistance that can be offered!

BTW, it does not appear to be related to gracefulStop. I increased it with this:

  scenarios: {
    test: {
      executor: 'constant-vus',
      vus: 1,
      duration: '100s',
      gracefulStop: '60s',
    },
  },
};

and while it now reports as a minute, the browser still closes precisely at 30 seconds.

Hi @mcw

It could be issue with the script, where it not able to perform some action or page is not completely loaded.

you can add some wait there e.g. sleep(30) or page.waitForNavigation() before performing the step where its failing

It is definitely a timing issue, because it will fail in different places, but always exactly at 30 seconds.

If I change the order of the pages that are loaded, the one that fails will be the one that’s up when the script has been running for 30 seconds.

It’s just a long chain of promises, and I’ve tried reordering. That’s how I figured out it was a timing issue and not something specific to a page:

 page
    .goto(`${host_port}${url}`, { waitUntil: "networkidle" })
    .then(() => {
      page.locator('input[name="inputusername"]').type(username);
      page.locator('input[name="inputpassword"]').type([password]);
      page.screenshot({ path: "login-screenshot.png" });
      page.locator('//button[text()=" Login"]').click();
    })
    .then(() => {
      url = "/r/apps/assets";
      console.log(`Bringing up ${url}`);
      page.goto(`${host_port}${url}`, { waitUntil: "networkidle" });
      sleep(page_load_wait);
      page.screenshot({ path: "assets-screenshot.png" });
    })
    .then(() => {
      url = "/r/apps/assessments";
      console.log(`Bringing up ${url}`);
      page.goto(`${host_port}${url}`, { waitUntil: "networkidle" });
      sleep(page_load_wait);
      page.screenshot({ path: "assessments-screenshot.png" });
    })
    .then(() => {
      url = "/r/apps/incidents";
      console.log(`Bringing up ${url}`);
      page.goto(`${host_port}${url}`, { waitUntil: "networkidle" });
      sleep(page_load_wait);
      page.screenshot({ path: "incidents-screenshot.png" });
    })

I’ve tried setting all the timings that I can think of, but to no avail…

  const context = browser.newContext();
  context.setDefaultNavigationTimeout(120000);
  context.setDefaultTimeout(120000);

Hi @mcw

Can you try returning promise after clicking on login. I have written script as below and it works-

export function webtest() {
  const browser = chromium.launch({
    args: [],                   // Extra commandline arguments to include when launching browser process
    debug: true,                // Log all CDP messages to k6 logging subsystem
    devtools: true,             // Open up developer tools in the browser by default
    env: {},                    // Environment variables to set before launching browser process
    executablePath: null,       // Override search for browser executable in favor of specified absolute path
    headless: true,            // Show browser UI or not
    ignoreDefaultArgs: [],      // Ignore any of the default arguments included when launching browser process
    proxy: {},                  // Specify to set browser's proxy config
    slowMo: '1000ms',            // Slow down input actions and navigations by specified time
    timeout: '60s',             // Default timeout to use for various actions and navigations
  });
  const page = browser.newPage();
  page.goto(Utils.getBaseUrl() , { waitUntil: 'load' }).
    then(() => {
      page.locator(selectors.loginPage.username).type('')
      page.locator(selectors.loginPage.password).type('')
      return Promise.all([
        page.waitForNavigation(),
        page.locator(loginBtn).click()
      ]).then(() => {
        page.screenshot({ path: 'screenshots/01_login.png' }),
          page.waitForSelector(""),
          page.locator("").type('')
        page.waitForSelector('')
        return Promise.all([
          page.waitForNavigation(),
          page.locator("").click()
        ])
      }).then(() => {
        page.waitForSelector(""),
          check(page, {
            'Patient Page': page.locator("").textContent() == '',
          });
        page.screenshot({ path: 'screenshots/02.png' })
      }).finally(() => {
        page.close();
        browser.close();
      })
    })

@vish_s02 – thanks for your input!

I’m a backend developer – not a front end one – but I think I’ve done what you’ve suggested, and the browser is still always forcibly closing at 30 seconds. (In fact, things seems to get a little worse, in that the script stops loading pages after the third one and just waits for the forcible close!)

Has anyone seen a browser stay up longer than a 30 second run time? If so, would they be willing to share the script?

  page
    .goto(`${host_port}${url}`, { waitUntil: "networkidle" })
    .then(() => {
      return Promise.all([
        page.locator('input[name="inputusername"]').type(username),
        page.locator('input[name="inputpassword"]').type([password]),
        page.screenshot({ path: "login-screenshot.png" }),
        page.waitForNavigation(),
        page.locator('//button[text()=" Login"]').click(),
      ]);
    })
    .then(() => {
      url = "/r/apps/assets";
      console.log(`Bringing up ${url}`);
      return Promise.all([
        page.goto(`${host_port}${url}`, { waitUntil: "networkidle" }),
        sleep(page_load_wait),
        page.screenshot({ path: "assets-screenshot.png" }),
      ]);
    })
    .then(() => {

Hi @mcw

Can you have one more try, seems you havnt return promise at correct place

export function webtest() {
  const browser = chromium.launch({
    args: [],                   // Extra commandline arguments to include when launching browser process
    debug: true,                // Log all CDP messages to k6 logging subsystem
    devtools: true,             // Open up developer tools in the browser by default
    env: {},                    // Environment variables to set before launching browser process
    executablePath: null,       // Override search for browser executable in favor of specified absolute path
    headless: true,            // Show browser UI or not
    ignoreDefaultArgs: [],      // Ignore any of the default arguments included when launching browser process
    proxy: {},                  // Specify to set browser's proxy config
    slowMo: '1000ms',            // Slow down input actions and navigations by specified time
    timeout: '60s',             // Default timeout to use for various actions and navigations
  });
  const page = browser.newPage();
  page
    .goto(`${host_port}${url}`, { waitUntil: "networkidle" })
    .then(() => {
      page.locator('input[name="inputusername"]').type(username),
        page.locator('input[name="inputpassword"]').type([password]),
        page.screenshot({ path: "login-screenshot.png" })
      return Promise.all([
        page.waitForNavigation(),
        page.locator('//button[text()=" Login"]').click(),
      ]).then(() => {
        url = "/r/apps/assets";
        console.log(`Bringing up ${url}`);
        page.goto(`${host_port}${url}`, { waitUntil: "networkidle" })
          .then(() => {
            page.screenshot({ path: "assets-screenshot.png" })
          })
      }).then(() => {
        url = "/r/apps/assets";
        console.log(`Bringing up ${url}`);
        page.goto(`${host_port}${url}`, { waitUntil: "networkidle" })
          .then(() => {
            page.screenshot({ path: "assets-screenshot.png" })
          })
      }).finally(() => {
        page.close();
        browser.close();
      })
    })

Welcome @mcw,

What error are you seeing? Could you paste the full terminal output?

Cheers,
Ankur

EDIT: If you are testing a publicly accessible website, could you paste the link to it too?

Hi @ankur I need also the browser keeps open to completely test the end2end scenario unfortunately on the next page the browser will suddenly close. You may replicate below script.

import { chromium } from 'k6/experimental/browser';
import { sleep } from 'k6';

export const options = {
    scenarios: {
        browser: {
            executor: 'per-vu-iterations',
            exec: 'browserTest',
            vus: 1
        }
    }
};
export async function browserTest() {
    const browser = chromium.launch({ headless: false });
    const page = browser.newPage();
    page.setViewportSize({
        width: 1366,
        height: 768
    });

    await page.goto('https://www.legalmatch.com/');

    // Select Category
    const category = page.locator("//button[normalize-space()='Choose a Category']");
    await category.click();
    const categoryItem = page.locator(
        "//div[@class='case-intake-form__dropdown-menu dropdown-menu js-case-intake-categories-dropdown is-single-choice']//div[1]"
    );
    await categoryItem.click();

    const zipLoc = page.locator("(//input[@placeholder='ZIP Code or Location'])[1]");
    await zipLoc.type('00001');
    await page.waitForTimeout(5000);

    const searchBtn = page.locator(
        "(//button[@class='case-intake-form__field-item case-intake-form__submit case-intake-form__submit--linear-gradient-576dc6-35499c js-intake-form-submit js-sticky-banner-trigger'])[1]"
    );
    await searchBtn.click();

    // Select Sub Category
    const checkBoxSubCat = page.locator("//input[@value='3'][1]");
    await Promise.all([checkBoxSubCat.check()]);
    const nextBtn = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    await Promise.all([page.waitForNavigation(), nextBtn.click()]);

    // Issue-Specific Questions
    sleep(30);
    const nextBtnQ = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    await Promise.all([page.waitForNavigation(), nextBtnQ.click()]);

    await page.waitForTimeout(10000);

    // Description

    /*check(page, {
        'cart item name': (page) =>
            page.locator("(//h1[normalize-space()='Choose a Subcategory:'])[1]").isVisible() ===
            true
    });*/
    
}

Hi @carlo,

Thanks for the test script. I was able to replicate the issue on my side too.

What’s happening is that the test is timing out after 30 seconds. In the latest version of k6, there is still a bug where a timeout on the BrowserType (chromium) is used globally, when it’s only meant to be used for the launch or connect methods on chromium. For now the fix is to set a higher timeout (60s worked for me):

const browser = chromium.launch({ headless: false, timeout: '60s' });

Give it ago and let us know if that works or not.

Cheers,
Ankur

Hi @ankur

I still encounter the error below.

ERRO[0020] communicating with browser: read tcp 127.0.0.1:61913->127.0.0.1:61912: wsarecv: An existing connection was forcibly closed by the remote host.  category=cdp elapsed="0 ms" goroutine=87
ERRO[0020] process with PID 24232 unexpectedly ended: exit status 1  category=browser elapsed="43 ms" goroutine=84           5)=641.19ms

Hi @carlo,

Did you close the browser at the end of the test with browser.close()?

Cheers,
Ankur

Hi, @ankur

The issue has been resolve by setting a higher timeout then I can proceed to end to end load test at the last page.

✓ Should be at Homepage
✓ Should be at Subcategory Page
✓ Should be at Description Page
✓ Should be at Save Your Case Page
✓ Should be at Cost Estimate Page
✓ Case should be posted!

 const browser = chromium.launch({ headless: false, timeout: '10m' });

However in the logs will show below error:

ERRO[0100] communicating with browser: read tcp 127.0.0.1:61397->127.0.0.1:61396: wsarecv: An existing connection was forcibly closed by the remote host.  category=cdp elapsed="73351 ms" goroutine=27
ERRO[0100] cleaning up the user data directory: remove C:\Users\LMPH\AppData\Local\Temp\xk6-browser-data-4251929022\Default\Affiliation Database: The process cannot access the file because it is being used by another process.  category="BrowserType:Launch" elapsed="77 ms" goroutine=74

running (01m39.5s), 0/1 VUs, 1 complete and 0 interrupted iterations
browser ✓ [======================================] 1 VUs  01m39.5s/10m0s  1/1 iters, 1 per VU

running (01m39.5s), 0/1 VUs, 1 complete and 0 interrupted iterations
browser ✓ [======================================] 1 VUs  01m39.5s/10m0s  1/1 iters, 1 per VU
ERRO[0100] process with PID 28956 unexpectedly ended: exit status 1  category=browser elapsed="5 ms" goroutine=24

Hi @carlo,

In the test script that you’ve shared, you’re not closing the browser, here’s the snippet of the end of the test script from above:

    sleep(30);
    const nextBtnQ = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    await Promise.all([page.waitForNavigation(), nextBtnQ.click()]);

    await page.waitForTimeout(10000);

    // Description

    /*check(page, {
        'cart item name': (page) =>
            page.locator("(//h1[normalize-space()='Choose a Subcategory:'])[1]").isVisible() ===
            true
    });*/
    
}

If you add browser.close at the end of the script, do you still see that error?

    sleep(30);
    const nextBtnQ = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    await Promise.all([page.waitForNavigation(), nextBtnQ.click()]);

    await page.waitForTimeout(10000);

    // Description

    /*check(page, {
        'cart item name': (page) =>
            page.locator("(//h1[normalize-space()='Choose a Subcategory:'])[1]").isVisible() ===
            true
    });*/

    browser.close(); // <---- close the browser here to prevent the error
}

Cheers,
Ankur