sitespeed.io/docs/documentation/sitespeed.io/scripting/index.md

17 KiB

layout title description keywords nav category image twitterdescription
default Use scripts in sitespeed.io to measure a user journey. With scripts you can simulate a user visiting to miltiple pages, clicking on links, log in, adding items to the cart ... yeah do whatever you want! selenium, web performance, sitespeed.io documentation sitespeed.io https://www.sitespeed.io/img/sitespeed-2.0-twitter.png Use scripts in sitespeed.io to measure a user journey.

Documentation / Scripting

Scripting

{:.no_toc}

  • Lets place the TOC here {:toc}

Test by scripting

The user journey

Test by scripting was introduced in sitespeed.io 8.0 and Browsertime 4.0 and makes it possible to measure a user journey. A user visiting multiple pages, clicking on links, log in, adding items to the cart ... yeah do whatever you want.

Scripting work the same in Browsertime and sitespeed.io, the documentation here are for both of the tools.

Your script will get access to two objects: The context object that holds information about the current run and the commands object that has commands/shortcuts to navigate in the page.

The simplest version of a script looks like this:

module.exports = async function(context, commands) {
  // add your own code here
}

Inside of that function you can use the context and commands objects.

The context object:

  • options - All the options sent from the CLI to Browsertime.
  • log - an instance to the log system so you can log from your navigation script.
  • index - the index of the runs, so you can keep track of which run that is running.
  • storageManager - The Browsertime storage manager that can help you get read/store files to disk.
  • selenium.webdriver - The Selenium WebDriver public API object.
  • selenium.driver - The instantiated version of the WebDriver driving the current version of the browser.

The commands object:

  • navigate(URL) - Use this if you want to use the exact way as Browsertime navigates to a new URL (same settings with pageCompleteCheck etc). But that URL will not be measured automatically.
  • measure.start(URL) - Start measuring and navigate to a new page in one go and measure.
  • measure.start(URL,alias) - Start measuring and navigate to a new page in one go and measure. And register an alias for that URL.
  • measure.start() - Use this when you want to start to measure a page. This will start the video and prepare everything to collect metrics. But it will not navigate to the URL.
  • measure.start(alias) - Use this when you want to start to measure a page. This will start the video and prepare everything to collect metrics. But it will not navigate to the URL. The next URL that will be accessed will get the alias.
  • measure.stop() - Collect metrics for a page.

And then you have a couple of help commands:

  • wait on a id to appear or wait x amountt of ms.
  • click on a link and/or wait for the next page to load.
  • js - run JavaScript in the browser.
  • switch to another frame or windo.

Run

You run your script by passing it to sitespeed.io and adding the parameter --multi. If you have multiple scripts, you can just pass them on too.

docker run --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:{% include version/sitespeed.io.txt %} script.js script2.js script3.js --multi

Examples

Here are a couple of examples on how you can use the scripting capabilities.

Measure the actual login step

module.exports = async function(context, commands) {
  // Navigate to a URL and do not measure the URL
  await commands.navigate(
    'https://en.wikipedia.org/w/index.php?title=Special:UserLogin&returnto=Main+Page'
  );
 
  try {
    // Add text into an input field, finding the field by id
    await commands.addText.byId('login', 'wpName1');
    await commands.addText.byId('password', 'wpPassword1');

    // Start the measurement and give it the alias login
    // The alias will be useds when the metrics is sent to 
    // Graphite/InfluxDB
    await commands.measure.start('login');

    // Find the sumbit button and click it and wait for the
    // page complete check to finish on the next loaded URL
    await commands.click.byIdAndWait('wpLoginAttempt');
    // Stop and collect the metrics
    return commands.measure.stop();
  } catch (e) {
    // We try/catch so we will catch if the the input fields can't be found
    // The error is automatically logged in Browsertime an rethrown here
  }
};

Measure the login step and more pages

module.exports = async function(context, commands) {
  // We start by navigating to the login page. 
  await commands.navigate(
    'https://en.wikipedia.org/w/index.php?title=Special:UserLogin&returnto=Main+Page'
  );
  
  // When we fill in a input field/click on a link we wanna
  // try/catch that if the HTML on the page changes in the feature
  // sitespeed.io will automatically log the error in a user friendly
  // way, and the error will be re-thrown so you can act on it.
  try {
    // Add text into an input field, finding the field by id
    await commands.addText.byId('login', 'wpName1');
    await commands.addText.byId('password', 'wpPassword1');
  
    // Start the measurement before we click on the 
    // submit button. Sitespeed.io will start the video recording
    // and prepare everything.
    await commands.measure.start('login');
    // Find the sumbit button and click it and then wait 
    // for the pageCompleteCheck to finish
    await commands.click.byIdAndWait('wpLoginAttempt');
    // Stop and collect the measurement before the next page we want to measure
    await commands.measure.stop();
    // Measure the Barack Obama page as a logged in user
    await commands.measure.start(
      'https://en.wikipedia.org/wiki/Barack_Obama'
    );
    // And then measure the president page
    return commands.measure.start('https://en.wikipedia.org/wiki/President_of_the_United_States');
  } catch (e) {
    // We try/catch so we will catch if the the input fields can't be found
    // The error is automatically logged in Browsertime and re-thrown here
  }
};

Measure one page after you logged in

Testing a page after you have logged in: First create a script that logs in the user (login.js):

module.exports = async function(context, commands) {
  await commands.navigate(
    'https://en.wikipedia.org/w/index.php?title=Special:UserLogin&returnto=Main+Page'
  );

  try {
    await commands.addText.byId('login', 'wpName1');
    await commands.addText.byId('password', 'wpPassword1');
    // Click on the submit button with id wpLoginAttempt
    await commands.click.byIdAndWait('wpLoginAttempt');
    // wait on a specific id to appear on the page after you logged in
    return commands.wait.byId('pt-userpage', 10000);
  } catch (e) {
    // We try/catch so we will catch if the the input fields can't be found
    // The error is automatically logged in Browsertime and re-thrown here
};

Then access the page that you want to test:

sitespeed.io --preScript login.js https://en.wikipedia.org/wiki/Barack_Obama

More complicated login example

module.exports = async function(context, command) {
  await command.navigate(
    'https://example.org'
  );
  try {
    // Find the sign in button and click it
    await command.click.byId('sign_in_button');
    // Wait some time for the page to open a new login frame
    await command.wait.byTime(2000);
    // Switch to the login frame
    await command.switch.toFrame('loginFrame');
    // Find the usenrmane fields by xpath (just as an example)
    await command.addText.byXpath(
      'peter@example.org',
      '//*[@id="userName"]'
    );
    // Click on the necx buttin
    await command.click.byId('verifyUserButton');
    // Wait for the gui to display the password field so we can select it
    await command.wait.byTime(2000);
    // Wait for the actual password field
    await command.wait.byId('password', 5000);
    // Fill in the password
    await command.addText.byId('dejh8Ghgs6ga(1217)', 'password');
    // Click the submit button
    await command.click.byId('btnSubmit');
    // In your implementation it is probably better to wait for an id
    await command.wait.byTime(5000);
    // Measure the next page as a logged in user
    return  command.measure.start(
      'https://example.org/logged/in/page'
  );
  } catch(e) {
     // We try/catch so we will catch if the the input fields can't be found
  }
};

Measure multiple pages

Test multiple pages in a script:

module.exports = async function(context, commands) {
  await commands.measure.start('https://www.sitespeed.io');
  await commands.measure.start('https://www.sitespeed.io/examples/');
  return commands.measure.start('https://www.sitespeed.io/documentation/');
};

Log from your script

You can log to the same log output as sitespeed.io:

module.exports = async function(context, commands) {
  context.log.info('Info logging from your script');
  context.log.error('Error logging from your script');
};

Pass your own options to your script

You can add your own parameters to the options object (by adding a parameter) and then pick them up in the script. The scripts runs in the context of browsertime, so you need to pass it on in that context.

For example: you wanna pass on a password to your script, you can do that by adding --browsertime.my.password MY_PASSWORD and then in your code get hold of that with:

module.exports = async function(context, commands) {
  // We are in browsertime context so you can skip that from your options object
  context.log.info(context.options.my.password);
}

Commmands

All commands will return a promise and you should await it to fullfil. If some command do not work, we will log that automatically and rethrow the error, so you can catch that and can act on that.

Measure

The measure command will prepare everything for measuring a new URL (clearing internal metrics, starting the video etc). If you give an URL to the measure command it will start to measure and navigate to that URL.

If you do not give it a URL, it will prepare everything and start the video. So it's up to you to navigate/click on a link/submit the page. You also need to stop the measurement so that Browsertime/sitespeed.io knows that you want the metrics.

measure.start(url)

Start and navigate to the URL and then automatically call the stop() function after the page has stopped navigating decided by the current pageCompleteCheck.

measure.start(url, alias)

Start and navigate to the URL and then automatically call the stop() function after the page has stopped navigating decided by the current pageCompleteCheck. The page will also get the alias that will be used when you send the metrics to Graphite/InfluxDB. Use it when you have complex URLs.

measure.start()

Start to measure. Browsertime/sitespeed.io will pick up the next URL and measure that. You need to call the stop() function yourself.

measure.start(alias)

Start to measure. Browsertime/sitespeed.io will pick up the next URL and measure that. You need to call the stop() function yourself. The page will also get the alias that will be used when you send the metrics to Graphite/InfluxDB. Use it when you have complex URLs.

measure.stop()

Stop measuring. This will collect technical metrics from the browser, stop the video recording, collect CPU data etc.

Click

The click command will click on links.

All click commands have two different versions: One that will return a promise when the link has been clicked and one that will return a promise that will be fullfilled when the link has been clicked and the browser navigated to the new URL and the pageCompleteCheck says ok.

If it do not find the link, it will throw an error, so make sure to catch if you want an alternative flow. {: .note .note-warning}

click.byName(name)

Click on element that is found by name attribute that has the given value.

click.byNameAndWait(name)

Click on element that is found by name attribute that has the given value and wait for the pageLoadCompoleteCheck to happen.

click.byClassName(className)

Click on element that is found by specific class name.

click.byClassNameAndWait(className)

Click on element that is found by specific class name and wait for page load complete check to finish.

click.byLinkText(text)

Click on link whose visible text matches the given string.

click.byLinkTextAndWait(text)

Click on link whose visible text matches the given string and wait for pageCompleteCheck to finish.

click.byPartialLinkText(text)

Click on link whose visible text contains the given substring.

click.byPartialLinkTextAndWait(text)

Click on link whose visible text contains the given substring and wait for pageCompleteCheck to finish.

click.byXpath(xpath)

Click on link that matches a XPath selector.

click.byXpathAndWait(xpath)

Click on link that matches a XPath selector and wait for page load complete check to finish.

click.byJs(js)

Click on a link located by evaluating a JavaScript expression. The result of this expression must be an element or list of elements.

click.byJsAndWait(js)

Click on a link located by evaluating a JavaScript expression. The result of this expression must be an element or list of elements. And wait for page complete check to finish.

click.byId(id)

Click on link located by the ID attribute. This locator uses the CSS selector *[id="$ID"], not document.getElementById.

click.byIdAndWait(id)

Click on link located by the ID attribute. This locator uses the CSS selector *[id="$ID"], not document.getElementById. And wait for page complete check to finish.

Wait

There are two help commands that makes it easier to wait. Either you can wait on a specific id to appear or for x amount of milliseconds.

wait.byTime(ms)

Wait for x ms.

wait.byId(id,maxTime)

Wait for an element with id to appear before maxTime. If the elemet do not appear within maxTime an error will be thrown.

byXpath(xpath, maxTime) {

Wait for an element founmd by xpath to appear beforeYo maxTime. If the elemet do not appear within maxTime an error will be thrown.

Run JavaScript

You can run your own JavaScript in the browser from your script.

js.run(javascript)

Run JavaScript. Will throw an error if the JavaScript fails.

js.runAsync(javascript)

Run async JavaScript. Will throw an error if the JavaScript fails.

Navigate

Navigate/go to a URL without measuring it.

navigate(url)

Navigate to a URL and do not measure it. It will use the default pageCompleteCheck.

Add text

You can add text to input elements.

addText.byId(text, id)

Add the text to the element with the id. If the id is not found the command will throw an error.

byXpath(text, xpath)

Add the text to the element by using xpath. If the xpath is not found the command will throw an error.

Switch

You can switch to iframes or windows if that is needed.

If frame/window is not found, an error will be thrown. {: .note .note-warning}

toFrame(id)

Switch to a frame by its id.

toWindow(name)

Switch to window by name.

toParentFrame

Switch to the parent frame.

Use Selenium directly

You can use Selenium directly if you need to use things that are not availible through our commands.

You get hold of the Selenium objects through the context. The selenium.webdriver that is the Selenium WebDriver public API object. And selenium.driver that's the instantiated version of the WebDriver driving the current version of the browser.

Checkout this example to see how you can use them.

module.exports = async function(context, commands) {
  // we fetch the selenium webdriver from context
  const webdriver = context.selenium.webdriver;
  const driver = context.selenium.driver;
  // before you start, make your username and password
  const userName = 'YOUR_USERNAME_HERE';
  const password = 'YOUR_PASSWORD_HERE';
  const loginForm = driver.findElement(webdriver.By.css('form'));
  const loginInput = driver.findElement(webdriver.By.id('wpName1'));
  loginInput.sendKeys(userName);
  const passwordInput = driver.findElement(webdriver.By.id('wpPassword1'));
  passwordInput.sendKeys(password);
  // this example skips waiting for the next page and validating that the login was successful.
  return loginForm.submit();
}

If you need help with Selenium, checkout the official Selenium documentation.