Wait is not working

Hi Team,

Came across a problem where none of the wait is working-
sleep(30)
waitForLoadState(‘domcontentloaded’)
waitForTimeout(10000);
waitForNavigation({ waitUntil: ‘networkidle’ })

I have written a script where after clicking a link it opens other page which take some time to load…but after clicking on like, after loading some content of page, browser abnormal close with below error -

communicating with browser: websocket: close 1006 (abnormal closure): unexpected EOF  category=cdp elapsed="4899 ms" goroutine=184 iteration_id=ceea2a010e6aaa67

the last command shown in logs before it crash -

fid:1D04C5F52146B3CA7D4DDA0EFEC25BCA furl:"pageurl" rid:31124.325  category="Frame:deleteRequest" elapsed="0 ms" goroutine=577 iteration_id=ceea2a010e6aaa67

in order to manage that first I used all types of wait but nothing work, so finally I used sleep() and it worked-

page.waitForSelector(xpath),
        page.hover(xpath)
        page.waitForNavigation(),
        page.$(xpath).click(),
        sleep(30)
        page.waitForSelector(xpath)

Now I have build k6 using main branch as -

xk6 build --output xk6-browser --with github.com/grafana/xk6-browser@main

and now converted script as -

 await page.hover(xpath)
  await page.locator(xpath).click()
  //sleep(30)
  //await page.waitForLoadState('domcontentloaded')
  //await page.waitForTimeout(10000);
  //await page.waitForNavigation({ waitUntil: 'networkidle' })
  await page.waitForSelector(xpath)
  await page.locator(xpath).click(),

here I tried all the waits but nothing is working and browser is crashing with same error -

communicating with browser: websocket: close 1006 (abnormal closure): unexpected EOF  category=cdp elapsed="4899 ms" goroutine=184 iteration_id=ceea2a010e6aaa67

and the last command shown in logs before it crash -

fid:1D04C5F52146B3CA7D4DDA0EFEC25BCA furl:"pageurl" rid:31124.325  category="Frame:deleteRequest" elapsed="0 ms" goroutine=577 iteration_id=ceea2a010e6aaa67

Hi @vish_s02,

I’m unable to reproduce the issue. If you can paste the full test script (pointing to a publicly accessible website) then that could help me reproduce the issue.

This is the script that I ran, which worked for me:

import { chromium } from 'k6/x/browser';

export default async function () {
  const browser = chromium.launch({headless: false});
  const context = browser.newContext();
  const page = context.newPage();

  try {
    await page.goto('https://test.k6.io/flip_coin.php');
    
    const xpath = "input[value='Bet on heads!']";

    await page.hover(xpath);
    
    await Promise.all([
      page.waitForNavigation({ waitUntil: 'networkidle' }),
      page.locator(xpath).click()
    ]);
    
    await page.waitForSelector(xpath);

    await Promise.all([
      page.waitForNavigation({ waitUntil: 'networkidle' }),
      page.locator(xpath).click()
    ]);
  } finally {
    page.close();
    browser.close();
  }
}

Can you try this script and let me know how it goes?

Cheers,
Ankur

Hi, @ankur I’ve encountered the same issue the browser will untimely terminate, you can replicate below script.

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

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();
    await page.waitForTimeout(10000);

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

    // Issue-Specific Questions
    const nextBtnQ = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    await nextBtnQ.click();
    await page.waitForTimeout(10000);

    // Description

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

Hi @carlo,

When I ran this script, I actually received this:

ERRO[0030] Uncaught (in promise) GoError: checking "//input[@value='3'][1]": read tcp 127.0.0.1:64505->127.0.0.1:64504: wsarecv: An existing connection was forcibly closed by the remote host.

This error should be visible for you as well if you scroll up.

Here’s a modified version of your script that goes all the way through to the description page, with some comments (look for // TM):

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

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 = await page.locator("//button[normalize-space()='Choose a Category']");+

    // TM
    // 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();
    category.click(); // no need for await here

    const categoryToSelect = 'Employment';
    const categoryItem = page.locator(`//div[@class='case-intake-form__dropdown-menu dropdown-menu js-case-intake-categories-dropdown is-single-choice']//div[normalize-space()='${categoryToSelect}']`)
    categoryItem.click();

    // TM
    // const zipLoc = page.locator("(//input[@placeholder='ZIP Code or Location'])[1]");
    // await zipLoc.type('00001');
    // await page.waitForTimeout(5000);
    await page.locator('[aria-label="Zip Code or Location"]').fill('00001');
    await page.waitForSelector('.case-intake-form__location-checker--valid'); // wait for the green checkmark

    // TM
    //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]"
    //);
    const searchBtn = page.locator("//button[@data-aut='ci_submit-btn']"); // possibly a more stable selector
    // await searchBtn.click();
    // await page.waitForTimeout(10000);

    // as this is involves a page navigation, we should wait for the page to load like this:
    await Promise.all([
        page.waitForNavigation(),
        searchBtn.click()
    ]);

    // Select Sub Category
    // TM
    //const checkBoxSubCat = page.locator("//input[@value='3'][1]");
    //await checkBoxSubCat.check();

    // here, we can simply click on the label instead of the input element itself:
    const subCategory = 'Pensions and Benefits';
    page.locator(`//label[normalize-space()="${subCategory}"]`).click();

    //const nextBtn = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    //await nextBtn.click();
    //await page.waitForTimeout(15000);

    // now, we have another navigation. HOWEVER, there is an odd update to this page that appears to mess with clicking of the 'Next' button.
    // there's probably a more elegant way to do this, but it can be worked around with:
    await page.waitForTimeout(3000);
    
    // after this delay, we can repeat the same pattern as above:
    const nextBtn = page.locator("//button[@data-aut='ci_submit-btn']");

    await Promise.all([
        page.waitForNavigation(),
        nextBtn.click()
    ]);
    
    // this is important, because the next page *also* has the same element, so we need to wait for it to disappear
    // before attempting to interact with it on the next page
    
    // Issue-Specific Questions
    // const nextBtnQ = page.locator("//button[@data-aut='ci_submit-btn'][1]");
    // await nextBtnQ.click();
    // await page.waitForTimeout(10000);

    await Promise.all([
        page.waitForNavigation(),
        nextBtn.click()
    ]);

    // should now be on the "Describe your case" screen:
    check(page, {
        'description page loaded': () => page.locator("//label[text()='Describe your Case in Detail:']").isVisible()
    })

    // Description

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

Hi, Tom

Thank you for your response.

I try to replicate your script seems the page is not stable still will close untimely. I’m doing a happy path end to end load test.

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

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 = await page.locator("//button[normalize-space()='Choose a Category']");
    +(
      
        category.click()
    );

    const categoryToSelect = 'Family';
    const categoryItem = page.locator(
        `//div[@class='case-intake-form__dropdown-menu dropdown-menu js-case-intake-categories-dropdown is-single-choice']//div[normalize-space()='${categoryToSelect}']`
    );
    categoryItem.click();

    await page.locator('[aria-label="Zip Code or Locaton"]').fill('00001');
    await page.waitForSelector('.case-intake-form__location-checker--valid'); 
    const searchBtn = page.locator("//button[@data-aut='ci_submit-btn']"); 
    
    await Promise.all([page.waitForNavigation(), searchBtn.click()]);

    // Select Sub Category
    const subCategory = 'Adoptions';
    page.locator(`//label[normalize-space()="${subCategory}"]`).click();

    await page.waitForTimeout(10000);

    // after this delay, we can repeat the same pattern as above:
    const nextBtn = page.locator("//button[@data-aut='ci_submit-btn']");

    await Promise.all([page.waitForNavigation(), nextBtn.click()]);

    await page.waitForTimeout(10000);

    await Promise.all([page.waitForNavigation(), nextBtn.click()]);
    await page.waitForTimeout(10000);

    // should now be on the "Describe your case" screen:
    check(page, {
        'description page loaded': () =>
            page.locator("//label[text()='Describe your Case in Detail:']").isVisible()
    });

    // Description
    await page.waitForSelector("input[placeholder='State why you need an attorney.']");
    await page.locator("input[placeholder='State why you need an attorney.']").fill('LoadTest');
    await page
        .locator(
            "textarea[placeholder='State the highlights / major facts that support your claim.']"
        )
        .fill('Test');

    await Promise.all([page.waitForNavigation(), nextBtn.click()]);
    await page.waitForTimeout(3000);

    // Save Your Case
    await page.waitForSelector("input[name='firstName']");
    await page.locator("input[name='firstName']").fill('LoadTest');
    await page.locator("input[name='lastName']").fill('Test');
    await page.locator("input[(placeholder = '(555) 123-4567')]").fill('4354355345');
    await page.locator("input[name='email']").fill('none@legalmatch.com');
    await page.locator("input[(value = 'true')][(name = 'notifyBySms')]").click();
    await Promise.all([page.waitForNavigation(), nextBtn.click()]);
    await page.waitForTimeout(3000);

    page.close();
    browser.close();
}

What do you see in the console output?

Hi, Tom

Below error logs appeared.

ERRO[0030] communicating with browser: read tcp 127.0.0.1:54413->127.0.0.1:54412: wsarecv: An existing connection was forcibly closed by the remote host. category=cdp elapsed="8611 ms" goroutine=80 ERRO[0030] process with PID 18592 unexpectedly ended: exit status 1 category=browser elapsed="47 ms" goroutine=77 ERRO[0057] Uncaught (in promise) waiting for navigation: timed out after 30s executor=per-vu-iterations scenario=browser