Skip to content

Commit

Permalink
docs: Generate scripting documentation (#2059)
Browse files Browse the repository at this point in the history
* First go at generating JSDocs for scripting
  • Loading branch information
soulgalore authored Jan 6, 2024
1 parent ae4ec78 commit d9638bb
Show file tree
Hide file tree
Showing 70 changed files with 2,649 additions and 64 deletions.
12 changes: 12 additions & 0 deletions jsdoc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Scripting
Welcome to the powerful world of scripting with sitespeed.io! This feature unlocks the potential to simulate real user journeys, measure performance, and gather detailed metrics by interacting with web pages through custom scripts. Whether you're looking to analyze simple page loads or complex user interactions, our scripting functionality offers the tools you need.

## Key Features

* **User Journey Simulation**: Script entire user flows, from navigation to clicks and form submissions, to capture a realistic user experience.
* **Performance Metrics Collection**: Gather crucial data like load times, visual metrics, and more, for each step of your user journey.
* **Flexible Scripting Language**: Write scripts in NodeJS, using familiar JavaScript syntax and robust libraries.

## Getting Started

Dive into scripting with our [tutorials](Tutorial-01-Introduction.html), [examples](tutorial-09-Examples.html), and [documentation for all commands](Commands.html).
60 changes: 60 additions & 0 deletions jsdoc/jsdoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"source": {
"include": [
"./lib/core/engine/commands.js",
"./lib/core/engine/command/",
"./lib/core/engine/command/mouse/",
"./jsdoc/README.md"
],
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"templates":{
"default": {
"outputSourceFiles": false
}
},
"opts": {
"tutorials": "./jsdoc/tutorials/",
"destination": "../sitespeed.io/docs/documentation/sitespeed.io/scripting/tutorials",
"template": "node_modules/clean-jsdoc-theme",
"theme_opts": {
"homepageTitle": "Tutorials and documentation for scripting in Browsertime and sitespeed.io",
"title": "sitespeed.io scripting",
"default_theme": "light",
"base_url": "https://www.sitespeed.io/documentation/sitespeed.io/scripting/tutorials/",
"footer": "© <a href='https://www.sitespeed.io'>sitespeed.io</a> 2024",
"create_style": "article ul { list-style: disc }",
"sections": ["Tutorials", "Classes"],
"favicon": "https://www.sitespeed.io/img/ico/sitespeed.io-144.png",
"menu": [
{
"title": "Start",
"link": "."
},
{
"title": "Documentation",
"link": "https://www.sitespeed.io/documentation/sitespeed.io/"
}
],
"meta": [
{
"name": "author",
"content": "Peter Hedenskog"
},
{
"name": "description",
"content": "Tutorials and documentation for scripting in Browsertime and sitespeed.io"
},
{
"name": "keywords",
"content":"scripting, tutorial, sitespeed.io, browsertime, web performance"
}
]
}
},
"markdown": {
"hardwrap": false,
"idInHeadings": true
}
}
58 changes: 58 additions & 0 deletions jsdoc/tutorials/01-Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Scripting in sitespeed.io and Browsertime allows you to measure user journeys by interacting with web pages. This feature is essential for simulating real-user interactions and collecting performance metrics for complex workflows. Scripting works the same in both Browsertime and sitespeed.io.

## Simple script
Here's a basic script example to start with:

```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
context.log.info('Start to measure my first URL');
return commands.measure.start('https://www.sitespeed.io');
}
```

To run this script, use the command `sitespeed.io -n 1 --multi measure.mjs`. This script measures the performance of https://www.sitespeed.io.

You can see that you get two helper objects in that function. The browsertime context and browsertime commands. Lets talk about those helper objects.

## Helpers
Lets start with the command object.

### The Commands Object

[Commands](Commands.html) are helpers for interacting with web pages.

Inside your script, you access them as properties on *commands.*. You can use them to measure a URL like `await commands.measure.start('https://www.example.com');`. Many of the commands are asynchronous so you need to await them.

You can see all the commands [ here](Commands.html).

### The Context Object

The `context` object in your script is a help object with access to the current context on when you run Browsertime/sitespeed.io. In most cases you do not need to use them (except the access to the log), but for special use cases they are handy.

The properties on the context object are:
- `options`: All options sent from the CLI to Browsertime/sitespeed.io. Here you can fetch paramters that you used when starting the test.
- `log`: An instance of the log system. Use it to log what you do.
- `index`: The index of the current run.
- `storageManager`: The manager that is used to read/store files to disk.
- `selenium.webdriver`: The public API object of Selenium WebDriver. You need it if you want to run Selenium scripts.
- `selenium.driver`: The instantiated WebDriver for the current browser session.

## Asynchronous commands
Many Browsertime commands are asynchronous, returning promises. You can see that when you look in the documencation and see *async* for the function.

Use await to ensure the script waits for an action to complete. When using multiple async operations, return the last promise to ensure the script waits until all operations are complete.

```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
await commands.navigate('https://www.sitespeed.io');
return commands.measure.start('https://www.sitespeed.io/documentation/');
}
```
68 changes: 68 additions & 0 deletions jsdoc/tutorials/02-Running-Scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Run scripts using Browsertime is easy. Create your script and run it like this:

```bash
browsertime myScript.mjs
```

And in sitespeed.io you need to add the `--multi` switch (test multiple pages).

```bash
sitespeed.io myScript.mjs --multi
```

## Multiple scripts

For multiple scripts, list them all in the command. This approach helps manage complex scripts by splitting them into multiple files.

```bash
sitespeed.io login.mjs measureStartPage.mjs logout.mjs --multi
```

Or you can break out code in multiple files.

Create a file to include *exampleInclude.mjs*

```JavaScript
export async function example() {
console.log('This is my example function');
}
```

Then include it *test.mjs*:

```JavaScript
import { example } from './exampleInclude.mjs';
export default async function (context, commands) {
example();
}
```
And then run it: `sitespeed.io --multi test.mjs`

## Add meta data to your script

You can add meta data like title and description to your script. The extra data will be visible in the HTML result page.

Setting meta data like this:

~~~javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
commands.meta.setTitle('Test Grafana SPA');
commands.meta.setDescription('Test the first page, click the timepicker and then choose <b>Last 30 days</b> and measure that page.');
await commands.measure.start(
'https://dashboard.sitespeed.io/d/000000044/page-timing-metrics?orgId=1','pageTimingMetricsDefault'
);
await commands.click.byClassName('gf-timepicker-nav-btn');
await commands.wait.byTime(1000);
await commands.measure.start('pageTimingMetrics30Days');
await commands.click.byLinkTextAndWait('Last 30 days');
await commands.measure.stop();
};
~~~

Will result in:

![Title and description for a script](https://www.sitespeed.io/img/titleanddesc.png)
139 changes: 139 additions & 0 deletions jsdoc/tutorials/03-Measurement-Commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
In sitespeed.io, measurements can be conducted using a meassure command for page navigation performance, gathering various metrics during page load. Alternatively, the StopWatch command is used for measuring arbitrary durations, such as the time taken for certain actions to complete. When you create your script you need to know what you want to measure.


## The `measure` Command

In web performance, *"navigation"* means switching from one webpage to another. This switch triggers several steps in your browser, like closing the current page, loading new content, and showing the new page fully loaded. In sitespeed.io, the commands.measure function is used to analyze this process. It tracks various performance details from the moment you start moving to a new page until it's completely loaded. This includes how long the page takes to load, how fast resources (like images and scripts) are loaded, and more, giving you a full picture of the navigation's performance.

Use the `measure` command for measuring page navigation performance. It captures metrics from the start to the end of a page load.

```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
return commands.measure.start('https://www.sitespeed.io');
}
```

If you use the measure command without a URL, it sets everything up and starts recording a video, but it doesn't navigate to a new page automatically. You need to manually navigate to a page, click a link, or submit a form. Remember to stop the measurement when you're done, so Browsertime/sitespeed.io can gather and report the performance data.

For example, to measure how long it takes to go from the sitespeed.io homepage to its documentation page, you would start by navigating to the sitespeed.io homepage. Then, you manually click on the link to the documentation page. Once you're on the documentation page, you stop the measurement to capture the performance metrics of this navigation.


```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {

await commands.navigate('https://www.sitespeed.io');

await commands.measure.start('Documentation');
// Using a xxxAndWait command will make the page wait for a navigation
await commands.click.byLinkTextAndWait('Documentation');
return commands.measure.stop();
}
```

The [measure command](Measure.html).

## The `stopWatch` Command

The `Stop Watch` command in sitespeed.io is used for measuring the time of activities other than web page navigation, like specific processes or user actions. You manually start and stop this watch to track the duration of these actions. When you use the Stop Watch, its timing data gets automatically linked to the web page you were analyzing right before you started the watch. This way, the time recorded by the Stop Watch becomes part of the performance data for that particular page.


```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
const stopWatch = commands.stopWatch.get('Before_navigating_page');
// Do the thing you want to measure ...
// Then stop the watch
const time = stopWatch.stop();
// Measure navigation to a page
await commands.measure.start('https://www.sitespeed.io');
// Then attach that timing to that page.
commands.measure.add(stopWatch.getName(), time);
}

```

The [stop watch command](StopWatch.html).

## Using user timings and element timings API

When testing a webpage you manage, it's a good idea to use the [User Timing](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/User_timing) and [Element Timing](https://wicg.github.io/element-timing/) APIs built into browsers. Most browsers support the User Timing API, and Chrome-based browsers support the Element Timing API. Browsertime and sitespeed.io automatically collect metrics from these APIs when you execute the measure command. If you need these metrics after navigating to a different page, you can also retrieve them using the JavaScript command within your script. This approach is helpful for gathering detailed timing information related to specific elements or user-defined timings on your page.

```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
await commands.navigate('https://www.sitespeed.io');

// The sitespeed.io start page has a user timing mark named userTimingHeader
const userTimingHeader = await commands.js.run(
`return performance.getEntriesByName('userTimingHeader')[0].startTime;`
);

// The sitespeed.io start page has a element timing api for the logo
const logoRenderTime = await commands.js.run(`
const observer = new PerformanceObserver(list => {});
observer.observe({ type: 'element', buffered: true });
const entries = observer.takeRecords();
for (let entry of entries) {
if (entry.identifier === 'logo') {
return Number(entry.renderTime.toFixed(0));
}
}
`);

context.log.info(
`User Timing header: ${userTimingHeader} ms and Logo Element render time ${logoRenderTime} ms`
);
}
```

## Measure a single page application (SPA)

Single Page Applications (SPAs) are web applications that load a single HTML page and dynamically update that page as the user interacts with the app. Unlike traditional web applications that reload the entire page or load new pages to display different content, SPAs rewrite the current page in response to user actions (a soft navigation), in best cases it
s leading to a more fluid user experience.

At the moment browser based metrics like first paint, largest contentful paint and others aren't updated for soft navigations. The Chrome team is [working on that](https://developer.chrome.com/docs/web-platform/soft-navigations-experiment) and we will try to implement that when its more mature. You can follow that work in [Browsertime issue #2000](https://github.com/sitespeedio/browsertime/issues/2000).

The best way to measure a SPA today is with the visual metrics that Browsertime collects by analysing the video recording. That way you can get metrics like first visual change and last visual change.

Here's an example on how to do that:

```javascript
/**
* @param {import('browsertime').BrowsertimeContext} context
* @param {import('browsertime').BrowsertimeCommands} commands
*/
export default async function (context, commands) {
await commands.measure.start('https://react.dev');
await commands.measure.start('Learn');
await commands.mouse.singleClick.byLinkTextAndWait('Learn');
return commands.measure.stop();
}
```

And run it like this:
`sitespeed.io react.mjs --multi --spa --video --visualMetrics`

If you are testing a SPA it's important to add the `--spa` switch. That helps Browsertime to setup some extra functionality to test that page (like instead of waiting for the onloadEvent to stop the measurement, it waits for silence in the network log).

The Chrome team has the following defintion of a Soft navigation:
1. The navigation is initiated by a user action.
2. The navigation results in a visible URL change to the user, and a history change.
3. The navigation results in a DOM change.

For Browsertime it's important that the URL change, that's how we know that we are measuruing a new page.

If your page do not change the URL or load any resources (JSON/JavaScript/CSS or images) when you do the "soft" navigation then you need to use the stop watch to measure that kind of navigation.
Loading

0 comments on commit d9638bb

Please sign in to comment.