grafana / k6

A modern load testing tool, using Go and JavaScript - https://k6.io
GNU Affero General Public License v3.0
25.92k stars 1.27k forks source link

k6/browser does not support true client performance testing #4036

Open jsaujla opened 2 days ago

jsaujla commented 2 days ago

Feature Description

k6/browser does not provide feature to capture individual transaction response time.

Below are some examples of true client (browser based) performance tests:

  1. The time taken to launch a website: I should be able to start a timer before launching the website then stop the timer when I would verify an element on the page visible/displayed.
  2. The time taken to redirect from one page to another page: I should be able to start a timer before clicking on a link then stop the timer when I would verify an element on the next page visible/displayed.
  3. Time taken to login: I should be able to start a timer before clicking on login button then stop the timer when I would verify an element on the next page (user dashboard page) visible/displayed.

Suggested Solution (optional)

Below is an example of JMeter WebDriver Plugin script (I executed the script with loop count 10):

//##### Config | Test Data #####
var maxWaitTime = 30;
var k6TestBaseUrl = "https://test.k6.io/";
var loginUser = "test_user";
var loginPassword = "1234";

//##### Selenium WebDriver Imports #####
var selenium = JavaImporter(org.openqa.selenium, org.openqa.selenium.support.ui);
var wait = new selenium.WebDriverWait(WDS.browser, java.time.Duration.ofSeconds(maxWaitTime));
var select = JavaImporter(org.openqa.selenium.support.ui.Select);
var actions = new org.openqa.selenium.interactions.Actions(WDS.browser);

//##### JMeter/WebDriver Script #####

WDS.sampleResult.sampleStart();
WDS.browser.manage().deleteAllCookies();
WDS.log.info("Sample Start - True_Client_Example_Scenario");

try {   
     // Launch test.k6.io
     WDS.sampleResult.subSampleStart('Launch_' + k6TestBaseUrl);
     WDS.browser.get(k6TestBaseUrl);
     wait.until(selenium.ExpectedConditions.elementToBeClickable(selenium.By.xpath("//a[@href='/my_messages.php']"))).click();     
     WDS.sampleResult.subSampleEnd(true);

     // Log in
     wait.until(selenium.ExpectedConditions.visibilityOfElementLocated(selenium.By.xpath("//input[@name='login']"))).sendKeys(loginUser);
     wait.until(selenium.ExpectedConditions.visibilityOfElementLocated(selenium.By.xpath("//input[@name='password']"))).sendKeys(loginPassword);

     WDS.sampleResult.subSampleStart('Testk6_Login');
     wait.until(selenium.ExpectedConditions.elementToBeClickable(selenium.By.xpath("//input[@value='Go!']"))).click();
     wait.until(selenium.ExpectedConditions.visibilityOfElementLocated(selenium.By.xpath("//h2[text()='Welcome, test_user!']")));  
     WDS.sampleResult.subSampleEnd(true);

     // Logout
     WDS.sampleResult.subSampleStart('Testk6_Logout');
     wait.until(selenium.ExpectedConditions.elementToBeClickable(selenium.By.xpath("//input[@value='Logout']"))).click();
     wait.until(selenium.ExpectedConditions.visibilityOfElementLocated(selenium.By.xpath("//input[@value='Go!']"))); 
     WDS.sampleResult.subSampleEnd(true);
}
catch (error) {
    WDS.log.error(error);
    WDS.sampleResult.subSampleEnd(false);
    throw error;
}
finally {
    WDS.sampleResult.sampleEnd();
}

Below is an overview of html output report: Image

Already existing or connected issues / PRs (optional)

No response

oleiade commented 2 days ago

For your eyes @inancgumus @ankur22 đź‘€

ankur22 commented 2 days ago

Hi @jsaujla,

Thanks for opening an issue.

please correct me if i've missed anything, but it sounds like you would like to time the individual navigations within a single test iteration, correct?

Would something like this suffice when working with custom metrics?:

import { browser } from 'k6/x/browser/async';
import { Trend } from 'k6/metrics';

export const options = {
  scenarios: {
    ui: {
      executor: 'shared-iterations',
      options: {
        browser: {
            type: 'chromium',
        },
      },
    },
  },
}

const launchTrend = new Trend('Launch');
const loginTrend = new Trend('Testk6_Login');
const logoutTrend = new Trend('Testk6_Logout');

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

  try {
    var start = Date.now();
    await page.goto('https://test.k6.io/', { waitUntil: 'networkidle' });
    await Promise.all([
      page.waitForNavigation(),
      page.locator('a[href="/my_messages.php"]').click(),
    ]);
    var end = Date.now();
    launchTrend.add(end - start);

    await page.locator('input[name="login"]').type('admin');
    await page.locator('input[name="password"]').type("123");

    start = Date.now();
    await Promise.all([
      page.waitForNavigation(),
      page.locator('input[type="submit"]').click(),
    ]);
    var end = Date.now();
    loginTrend.add(end - start);

    start = Date.now();
    await Promise.all([
      page.waitForNavigation(),
      page.locator('input[type="submit"]').click(),
    ]);
    var end = Date.now();
    logoutTrend.add(end - start);
  } finally {
    await page.close();
  }
}

When running it locally, the summary will show the custom metrics like so:

     Launch......................: avg=2375     min=2375    med=2375     max=2375     p(90)=2375     p(95)=2375    
     Testk6_Login................: avg=479      min=479     med=479      max=479      p(90)=479      p(95)=479     
     Testk6_Logout...............: avg=448      min=448     med=448      max=448      p(90)=448      p(95)=448