Writing A Scenario
To estimate the carbon footprint of your application, GreenFrame executes a Playwright scenario in a sandboxed container.
Your First Scenario
Create a JS file named as you wish - for instance, scenario.js
. It should contain an async function expecting one argument: a page
object.
Since version 2.0.0, this function has to be exported using module.exports
. (The import/export
syntax is not supported yet)
const scenario = async function (page) {
// Here interactions using the page object
};
module.exports = scenario;
Then, write user interactions as calls to the page
object method. Every interaction returns a promise, so be sure to await
them to run the scenario step by step.
Here is an example scenario:
const scenario = async (page) => {
await page.goto('', { waitUntil: 'networkidle' }); // Go to the baseUrl
await page.waitForTimeout(3000); // Wait for 3 seconds
await page.scrollToElement('footer'); // Scroll to the footer (if present)
await page.waitForNetworkIdle(); // Wait every request has been answered as a normal user.
};
module.exports = scenario;
How to convert a 1.x scenario into a 2.x scenario
const scenario = [previous_code]
module.exports = scenario;
Default Scenario
The default scenario opens the URL and scrolls to the end of the page. You can use it as the basis for your scenario.
const visit = async (page) => {
await page.goto('', { waitUntil: 'networkidle' });
await page.scrollToEnd();
};
module.exports = visit;
Adding Milestones
When you check the details of an analysis on the GreenFrame.io website, you may find it hard to relate a particular spike in the carbon footprint to a particular user interaction.
To help you understand what's going on, you can add milestones to your scenario. A milestone is a step that will be displayed in the analysis timeline.
Add a milestone by calling the page.addMilestone()
method in your scenario:
const scenario = async (page) => {
await page.goto('', { waitUntil: 'networkidle' });
await page.addMilestone('Go to solutions'); // here
await page.scrollToElement('#solutions'), await page.click('#solutions');
await page.waitForNetworkIdle();
await page.addMilestone('Go Back home'); // and here
await page.goto('', { waitUntil: 'networkidle' });
};
module.exports = scenario;
Using Environment Variables or External Library
In a scenario, you can import external module, either local JS files or npm libraries. This allows you to use advanced features of Playwright, specific code, or utility functions.
Here is an example of scenario using both external lib and environment variables to log in a website without hard-coding keys in the file :
const { loginToWebSite } = require('externalLoginLibrary');
const login = process.env.ENV_VAR_LOGIN;
const password = process.env.ENV_VAR_PASSWORD;
const scenario = async (page) => {
await loginToWebSite(login, password);
await page.goto('', { waitUntil: 'networkidle' });
await page.scrollToEnd();
await page.waitForNetworkIdle();
};
module.exports = scenario;
To use an external local file, import it using
require
(The
import/export
syntax is not supported yet)To use an external library, import it using
require
. It must be installed locally using your favorite package manager. For this, create apackage.json
file locally.
{
"name": "greenframe-myProject",
"description": "GreenFrame scenarios for myProject",
"version": "0.1.0",
"author": "Me",
"dependencies": {
"@playwright/test": "^1.30.0",
"externalLoginLibrary": "^0.1.0"
},
"license": "UNLICENSED"
}
Then, before runing greenframe, just run npm
or yarn
on this package.
~/myProject/yarn
~/myProject/greenframe analyze -C myConfiguration.yml
To use environment variables, you need to configure them using the
envFile
or theenvVar
optionCheck the Configuration File page for more details.
Playwright API
Check the PlayWright documentation on writing tests for more information.
Recording A Scenario
You can write scenarios by hand, or use the PlayWright Test Generator to generate a scenario based on a user session.
Custom Playwright Instance
GreenFrame's Playwright instance is tweaked in 2 ways:
- To better replicate real user interactions (which are much slower than what a robot does), new methods were added and some existing methods were overridden.
- To prevent malicious users from executing arbitrary shell scripts in the container, some methods were removed
Page Methods
The page
object exposed in GreenFrame scenarios is the PlayWright page
object augmented with a few methods improving the developer experience.
Here is a list of methods that only exists in GreenFrame's page
object.
page.scrollToElement(selector)
Scroll smoothly to an element.
selector <string>
: Selector of a DOM element. See Playwright selectors documentation for more details about the syntax.
Example:
await page.scrollToElement('footer');
page.scrollToEnd()
Scroll smoothly to the end of the page.
Example:
await page.scrollToEnd();
page.waitForNetworkIdle([state, options])
This method waits until networkIdle
.
- This is syntactic sugar for
page.waitForLoadState()
where the state is fixed atnetworkidle
await page.waitForNetworkIdle(); // The promise resolves after 'networkidle' event.
page.scrollByDistance(distance)
Scroll smoothly of the given offset in pixels
distance <number>
: Distance of the smooth scroll. Can either be positive (go down) or negative (go up).
Example:
await page.scrollByDistance(100); // go down by 100px
await page.scrollByDistance(-100); // go up by 100px
page.addMilestone(milestone)
You can add milestones to a scenario. These milestones will be displayed on analysis graphs and allow you to understand which user actions perform this peak on graphs.
Example:
await page.addMilestone('Go to home');
The milestone name must be unique in your scenario.
Best practices to write a good scenario
To have good stability in your scenario and detect changes during development, it is important to keep in mind some best practices by using GreenFrame CLI.
The main goal is to be sure that GreenFrame CLI can track every network request, record every page rendering, and collect the same amount of data for each subsequent analysis.
Typically, if you click on a button while the server has not responded to the request yet, The GreenFrame CLI might not track the entire network traffic.
If your page hasn't been entirely loaded, your CPU Usage will also be underestimated.
Use { waitUntil: 'networkidle' }
as often as possible
Avoid :
await page.goto('/path');
await page.waitForNavigation();
Prefer:
await page.goto('/path', { waitUntil: 'networkidle' });
await page.waitForNavigation({ waitUntil: 'networkidle' });
By default, a goto
function will be resolved when the page has fired the load
event.
The waitUntil
with networkidle
option force Playwright to resolve the function when no request has been opened in the last 500ms
and all requests have been answered.
Whenever you want to be sure your network has been fully tracked, you can use the following custom function:
await page.waitForNetworkIdle();
Use waitForNavigation
on Page Change
Most of the time your scenario will click on a button or a link to be redirected to another page. You can ensure that your navigation is terminated by using a Promise.all()
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.click('a'),
]);
The following line will be resolved when both waitForNavigation
and click
functions have been resolved.
It is important to put in first the waitForNavigation
function because it will tell Playwright to be ready for a page change.