-
-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Generate scripting documentation (#2059)
* First go at generating JSDocs for scripting
- Loading branch information
1 parent
ae4ec78
commit d9638bb
Showing
70 changed files
with
2,649 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/'); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Oops, something went wrong.