Ecojourney is an eco-design website audit tool focused on best practices over scoring.
π§ Ecojourney is in alpha stage π§
Everything is experimental and may change significantly at any time.
The NPM package hasn't been published yet, but it can be installed from the tarball generated by the
build
job. Download and extract thetarball.zip
file, then run:npm i -g ecojourney-X.Y.Z.tgz
Ecojourney analyses a browsing scenario on a web application, collecting metrics and issues about eco-design best practices.
It is made to help developers improve their web application by highlighting issues to fix in order to reduce its environmental impact, primarily on end-user devices and network infrastructures. But this is not enough to affirm that a web application is environmental friendly: an Ecojourney audit should be completed by automated and manual source code analysis, server-side application analysis (e.g. energy consumption metrology with Scaphandre), and should come after a rigorous UX design process (to focus on simple and useful features), Life-Cycle Assessments (ISO 14040 & 14044), and more.
Under the hood, this tool is built to be modular, extensible (coming soon!), and it uses a carefully selected small set of NPM dependencies (8, including transitive ones). Playwright is the main one, enabling cross-browser web automation, device emulation, proxy support and more.
Ecojourney is inspired by these tools, sharing several similarities, but trying to bring different features and/or various improvements:
- Lighthouse, but focuses on eco-design (even if there's some overlap with performance) and allows to analyse multiple pages and complex scenarios with a single run,
- GreenIT Analysis (CLI), but focuses on best practices over scoring and with a more refined API and tooling,
- EcoIndex, but focuses on best practices over scoring, with more metrics and bundled as a CLI tool instead of a web service.
π» Make sure your computer meets the following requirements:
- Node.js 18.x or newer
- A web browser: Google Chrome, Microsoft Edge or a Playwright supported browser
π¦ Install using npm
:
npm i -g ecojourney
π Audit a single URL:
ecojourney audit https://mywebsite.net
π Initialise a manifest file and run it:
ecojourney init mywebsite.yml
ecojourney audit mywebsite.yml
Ecojourney is published as an NPM package and can be installed in various ways as long as Node.js and your favorite package manager are installed on your workstation.
Install and run globally:
npm i ecojourney -g
ecojourney [command]
Install and run globally, using npx
:
npx ecojourney [command]
Install as a dev dependency of your Node.js project (recommended):
cd my-project/
npm i ecojourney -D
- Run locally:
npx ecojourney [command]
- Run locally, using a dedicated script in your
package.json
file:
{
"scripts": {
"ecojourney": "ecojourney audit ecojourney.yml"
}
}
npm run ecojourney
Initialise a manifest YAML file:
ecojourney init mywebsite.yml
Edit file content:
- Define the browsing scenario to audit
- Customise the configuration: browser, device, report formats, proxy, ...
Run the audit:
ecojourney audit mywebsite.yml
Some configuration options can be overridden from the CLI or environment variables:
export ECOJOURNEY_AUDIT_BROWSER="msedge"
ecojourney audit mywebsite.yml --dry-run
Ecojourney commands can be executed from a terminal:
ecojourney [options] [command]
Global options
Flags | Description | Environment variable |
---|---|---|
-V, --version |
Output the version number | |
-v, --verbose |
Enable verbose output | ECOJOURNEY_VERBOSE |
-h, --help |
Display help for command |
Commands
Name | Usage | Description |
---|---|---|
audit |
[options] <path> |
Audit a website eco-design compliance |
init |
[options] [path] |
Initialise a manifest file interactively |
Audit a website eco-design compliance.
ecojourney audit [options] <path>
Arguments
Name | Description | Required |
---|---|---|
path |
Path to the audit manifest file or website page URL | βοΈ |
Options
Flags | Description | Default | Environment variable |
---|---|---|---|
-b, --browser [browser] |
Browser to run the audit with | chromium |
ECOJOURNEY_AUDIT_BROWSER |
-l, --headless [headless] |
Run browser in headless mode | true |
ECOJOURNEY_AUDIT_HEADLESS |
-d, --device [device] |
Simulate browser behavior for a specific device (e.g. Galaxy S8) | ECOJOURNEY_AUDIT_DEVICE |
|
-H, --header [headers...] |
Additional HTTP headers to be sent with every request | ECOJOURNEY_AUDIT_HEADERS |
|
-t, --timeout [timeout] |
Maximum time to wait for navigations or actions, in milliseconds | ECOJOURNEY_AUDIT_TIMEOUT |
|
-r, --retry [retry] |
Number of retries in case of failure | 0 |
ECOJOURNEY_AUDIT_RETRY |
-o, --output [output] |
Directory to write reports to | . |
ECOJOURNEY_AUDIT_OUTPUT |
-f, --format [formats...] |
Output report formats | html,json |
ECOJOURNEY_AUDIT_FORMAT |
-s, --dry-run |
Simulate the audit without actually running the browser | false |
ECOJOURNEY_AUDIT_DRY_RUN |
-h, --help |
Display help for command |
Initialise a manifest file interactively.
ecojourney init [options] [path]
Arguments
Name | Description | Required |
---|---|---|
path |
Path to the audit manifest file to generate | π² |
Options
Flags | Description | Default | Environment variable |
---|---|---|---|
-h, --help |
Display help for command |
The manifest file allows to describe the audit scenario and configuration.
It can be easily and interactively initialised using the init
command. A JSON schema is also
provided to enable code completion and validation in your IDE (using built-in
JSON schema support for YAML files or via an extension).
# Audited website description
name: My website
description: A website that must comply with eco-design best practices
url: https://mywebsite.net
# Audit configuration (merged with CLI flags and environment variables)
config: ...
# Procedures allow to define reusable sequences of actions
procedures: ...
# Actions allow to define the browsing scenario to run and audit
actions:
# Start a scenario (allows to group page audit results)
- scenario: Browse blog posts
# Navigate to the posts index page
- goto: https://mywebsite.net/posts
- wait: main.posts
# Analyse the current page (including the previous navigation actions)
- page: Posts index
# Take a screenshot of the current page
- screenshot: posts-index.png
# Navigate to and audit the post page
- click: article.post
- wait: main.post
- page: Post
- screenshot: posts-single.png
Some properties support injecting environment variables and procedure arguments
using the double curly braces {{
and }}
as delimiters. This is particularly
useful to avoid committing sensitive values such as user passwords.
Example:
# Fill a password input using a value set from an environment variable
- fill: { selector: "#password", value: "{{ env.PASSWORD }}" }
name: My website
description: A website that must comply with eco-design best practices
url: https://mywebsite.net
Name | Description | Required |
---|---|---|
name |
Name of the web application | π² |
description |
Description of the web application or of the current test | π² |
url |
Main/root URL of the web application | βοΈ |
Audit configuration (merged with CLI flags and environment variables)
config:
browser: "chromium"
headless: true
device: "Galaxy S8"
headers: {"X-User":"user"}
proxy:
server: "http://myproxy.com:3128"
bypass: ".com, chromium.org, .domain.com"
username: "username"
password: "password"
timeout: 10000
retries: 3
output: "./reports/"
formats: ["html"]
influxdb:
url: "http://localhost:8086"
token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
org: "my-org"
bucket: "my-bucket"
prefix: "eco_"
dryRun: true
verbose: true
Name | Description | Type | Default | Values |
---|---|---|---|---|
browser |
Browser to run the audit with | string |
chromium |
msedge , chrome , chromium , firefox , webkit |
headless |
Run browser in headless mode | boolean |
true |
|
device |
Simulate browser behavior for a specific device | string |
||
headers |
Additional HTTP headers to be sent with every request | object |
||
proxy |
Network proxy settings | object |
||
β server |
Proxy to be used for all requests | string |
||
β bypass |
Comma-separated domains to bypass proxy | string |
||
β username |
Username to use if HTTP proxy requires authentication | string |
||
β password |
Password to use if HTTP proxy requires authentication | string |
||
timeout |
Maximum time to wait for navigations or actions, in milliseconds | integer |
||
retries |
Number of retries in case of failure | integer |
0 |
|
output |
Directory to write reports to | string |
. |
|
formats |
Output report formats | array |
html,json |
html , json , influxdb |
influxdb |
InfluxDB connection configuration | object |
||
β url |
Base URL | string |
||
β token |
Authentication token | string |
||
β org |
Destination organisation for writes | string |
||
β bucket |
Destination bucket for writes | string |
||
β prefix |
Measurement name prefix | string |
ecojourney_ |
|
dryRun |
Simulate the audit without actually running the browser | boolean |
false |
|
verbose |
Enable verbose output | boolean |
false |
Actions allow to define the browsing scenario to run and audit.
actions:
# Abbreviated syntax
- <name>: <value1>, <value2>
# Explicit syntax
- <name>:
<property1>: <value1>
<property2>: <value2>
Check or uncheck a checkbox or a radio button.
Abbreviated syntax
Argument: the selector to use when resolving the DOM element and the state to set (check
or uncheck
, default to check
)
- check: "input[name='remember'], check"
Explicit syntax
- check:
selector: "input[name='remember']"
state: "check"
Property | Type | Description | Required |
---|---|---|---|
selector |
string |
The selector to use when resolving the DOM element | βοΈ |
state |
string |
The state to set (check or uncheck , default to check ) |
π² |
Click on an element.
Abbreviated syntax
Argument: the selector to use when resolving the DOM element
- click: "button[type='submit']"
Explicit syntax
- click:
selector: "button[type='submit']"
Property | Type | Description | Required |
---|---|---|---|
selector |
string |
The selector to use when resolving the DOM element | βοΈ |
Fill an input or a textarea with a text.
Abbreviated syntax
Argument: the selector to use when resolving the DOM element and the value to set (supports templating)
- fill: "#search, Eco-design"
Explicit syntax
- fill:
selector: "#search"
value: "Eco-design"
Property | Type | Description | Required |
---|---|---|---|
selector |
string |
The selector to use when resolving the DOM element (supports templating) | βοΈ |
value |
string |
The value to set (supports templating) | βοΈ |
Navigate to the given URL.
Abbreviated syntax
Argument: the target HTTP/HTTPS URL (supports templating)
- goto: "https://mywebsite.net/admin"
Explicit syntax
- goto:
url: "https://mywebsite.net/admin"
Property | Type | Description | Required |
---|---|---|---|
url |
string |
The target HTTP/HTTPS URL (supports templating) | βοΈ |
Mark the current state as a stable page to analyse.
Abbreviated syntax
Argument: the page name
- page: "Profile page"
Explicit syntax
- page:
name: "Profile page"
Property | Type | Description | Required |
---|---|---|---|
name |
string |
The page name | βοΈ |
Execute actions from a procedure.
Abbreviated syntax
Argument: the procedure name
- procedure: "login"
Explicit syntax
- procedure:
name: "login"
args: {"username":"admin"}
Property | Type | Description | Required |
---|---|---|---|
name |
string |
The procedure name | βοΈ |
args |
object |
Procedure arguments | π² |
Start a scenario (allows to group page audit results).
Abbreviated syntax
Argument: the scenario name
- scenario: "Browse my profile"
Explicit syntax
- scenario:
name: "Browse my profile"
newContext: false
exclude: false
Property | Type | Description | Required |
---|---|---|---|
name |
string |
The scenario name | βοΈ |
newContext |
boolean |
Close the current context and create a new one | π² |
exclude |
boolean |
Exclude this scenario from analysis (only run actions) | π² |
Take a screenshot of the current page.
Abbreviated syntax
Argument: the file path to save the image to
- screenshot: "posts.png"
Explicit syntax
- screenshot:
path: "posts.png"
Property | Type | Description | Required |
---|---|---|---|
path |
string |
The file path to save the image to | βοΈ |
Scroll an element into view.
Abbreviated syntax
Argument: the selector to use when resolving the DOM element
- scroll: "#footer"
Explicit syntax
- scroll:
selector: "#footer"
Property | Type | Description | Required |
---|---|---|---|
selector |
string |
The selector to use when resolving the DOM element | βοΈ |
Select option or options in select.
Abbreviated syntax
Argument: the selector to use when resolving the DOM element and option(s) to select
- select: "select[name='lang'], fr"
Explicit syntax
- select:
selector: "select[name='lang']"
value: "fr"
values: ["fr","en"]
Property | Type | Description | Required |
---|---|---|---|
selector |
string |
The selector to use when resolving the DOM element | βοΈ |
value |
string |
The option to select | π |
values |
array |
Options to select | π |
Select input files for upload.
Abbreviated syntax
Argument: the selector to use when resolving the DOM element and input file(s) to set
- upload: "input[name='file'], myfile.txt"
Explicit syntax
- upload:
selector: "select[name='lang']"
file: "myfile.txt"
files: ["myfile1.txt","myfile2.txt"]
Property | Type | Description | Required |
---|---|---|---|
selector |
string |
The selector to use when resolving the DOM element | βοΈ |
file |
string |
The input file to set | π |
files |
array |
Input files to set | π |
Wait for the required load state to be reached or for an element to be visible.
Abbreviated syntax
Argument: the load state to wait for (load
, domcontentloaded
, networkidle
)
- wait: "load"
Argument: the selector to use when resolving the DOM element
- wait: "main#container"
Explicit syntax
- wait:
state: "load"
selector: "main#container"
Property | Type | Description | Required |
---|---|---|---|
state |
string |
The load state to wait for | π |
selector |
string |
The selector to use when resolving the DOM element | π |
Procedures allow to define reusable sequences of actions.
Define a procedure with a unique name under the procedures
key:
procedures:
# Log a user in using the login form page
login:
- page: "Login page for {{ args.username }}"
- fill: { selector: "#username", value: "{{ args.username }}" }
- fill: { selector: "#password", value: "{{ args.password }}" }
- click: button[type="submit"]
Call it from the browsing scenario using the procedure
action:
actions:
- ...
- procedure:
name: login
args: { username: user, password: "{{ env.USER_PASSWORD }}" }
- ...
Audit results can be exported using various report formats that can be specified
using the formats
CLI flag, environment variable or manifest configuration
key.
Export analysis results to an HTML report file.
β‘οΈ Recommended for simple analysis result visualisation
Export analysis results to a JSON report file.
β‘οΈ Recommended for further data analysis and visualisation
Write analysis results to an InfluxDB time-series database via 2 measurements:
{prefix}issues
: number of issues by severity for each audit, scenario and page, with analysis duration{prefix}measure
: main metrics for each audit, scenario and page
Published data is more or less the same as the data visible on the HTML report without opening collapsible elements, thus, some data (issues list, secondary measures, rules and metrics list, etc.) are not written because they doesn't fit well in a TSDB or are not relevant enough to be worth tracking on a dashboard.
β‘οΈ Recommended to keep track of analysis results over time on a Grafana dashboard
βοΈ Don't forget to set a retention time for the target bucket to enable automatic deletion of old data.
Audits can be executed in CI environments:
- Use the Playwright Docker image in a Linux agent:
mcr.microsoft.com/playwright
Provider | Configuration key |
---|---|
GitHub Actions | jobs.<job_id>.container |
GitLab | <job_id>.image |
Azure Pipelines | container or jobs[*].container |
- Install project dependencies, including Ecojourney:
npm ci
- Run the audit:
npx ecojourney audit [...]
βοΈ Best practices:
- Running the audit on each commit is usually wasteful, it should preferably be executed manually or on specific events (main branch, release, ...)
- Don't forget to cache dependencies (Docker image,
node_modules
/ NPM cache, ...)
Each page from the browsing scenario is analysed by a list of analysers, each one collecting metrics and identifying issues regarding eco-design best practices.
Check cache headers
Id | Name | Description |
---|---|---|
configure-cache-headers |
Configure cache headers | Configure cache headers on static resources to enable HTTP caching (Cache-Control with a large max-age, ETag and Last-Modified) |
Check compression
Id | Name | Description |
---|---|---|
compressible-requests-count |
Compressible requests | The number of requests that should be served compressed |
Id | Name | Description |
---|---|---|
enable-compression |
Enable compression | Configure HTTP compression to improve transfer speed and bandwidth utilisation |
Check cookies
Id | Name | Description |
---|---|---|
optimise-cookies |
Optimise cookies size | Optimise cookies size and remove them when they are useless |
no-cookie-for-static-resource |
No cookie for static resource | Host static resource on a domain without cookie |
Check for request and page errors
Id | Name | Description |
---|---|---|
failed-requests-count |
Failed requests | The number of failed requests |
errors-count |
Errors | The number of page errors |
Id | Name | Description |
---|---|---|
fix-error |
Fix error | Fix code or request error as it consumes resources uselessly |
Check for fonts
Id | Name | Description |
---|---|---|
external-fonts-count |
External fonts | The number of external fonts |
external-fonts-size |
External fonts size | The total size of external fonts |
Id | Name | Description |
---|---|---|
use-standard-fonts |
Use standard fonts | Use fonts already pre-installed on user terminals to avoid additional downloads, or at least optimise external font |
Global metrics calculation and analysis
Id | Name | Description |
---|---|---|
eco-index |
EcoIndex | The EcoIndex |
requests-count |
Requests | The number of HTTP requests |
responses-size |
Responses size | The total size of HTTP responses |
dom-elements-count |
DOM elements | The number of DOM elements |
greenhouse-gases-emission |
π«οΈ GhG emission | The greenhouse gases emission |
water-consumption |
π§ Water consumption | The water consumption |
redirections-count |
Redirections | The number of HTTP redirections |
domains-count |
Domains | The number of domains |
Id | Name | Description |
---|---|---|
reduce-requests-count |
Reduce requests count | Reduce the number of requests |
reduce-responses-size |
Reduce responses size | Reduce the responses size |
reduce-dom-size |
Reduce DOM size | Reduce the page complexity (and thus the number of elements in the DOM) |
avoid-redirections |
Avoid redirections | Avoid redirections as they increase response time and resource consumption uselessly |
limit-domains-count |
Limit domains count | Limit the number of domains serving resources |
Check images
Id | Name | Description |
---|---|---|
raster-images-count |
Raster images | The number of raster images |
raster-images-size |
Raster images size | The total size of raster images |
vector-images-count |
Vector images | The number of vector images |
vector-images-size |
Vector images size | The total size of vector images |
Id | Name | Description |
---|---|---|
optimise-image |
Optimise image | Replace raster images with CSS, font glyphs or vector images when possible, otherwise use the right format (WebP, AVIF, PNG) and compression |
optimise-vector-image |
Optimise vector image | Optimise and minimise SVG images |
serve-right-sized-image |
Serve right-sized image | Serve pre-resized image instead of resizing browser-side |
load-only-displayed-image |
Load only displayed image | Load image only if it is displayed |
Check plugins
Id | Name | Description |
---|---|---|
social-plugins-count |
Social plugins | The number of social plugin requests |
Id | Name | Description |
---|---|---|
avoid-social-plugin |
Avoid social plugin | Social network official plugins are usually heavy and intrusive, replace them with basic links |
Check scripts
Id | Name | Description |
---|---|---|
external-scripts-count |
External scripts | The number of external scripts |
external-scripts-size |
External scripts size | The total size of external scripts |
embedded-scripts-count |
Embedded scripts | The number of embedded scripts |
embedded-scripts-size |
Embedded scripts size | The total size of embedded scripts |
Id | Name | Description |
---|---|---|
externalise-script |
Externalise script | Avoid embedding script into the HTML page as it would be transferred each time the page is requested |
minify-script |
Minify script | Reduce the size of the script by minifying JS code |
reduce-scripts-count |
Reduce scripts count | Reduce the number of scripts: combine them to reduce the number of requests |
reduce-script-size |
Reduce script size | Reduce the size of the script: use JS only when necessary, remove dead code using tree shaking, configure compression, ... |
Check style sheets
Id | Name | Description |
---|---|---|
external-styles-count |
External styles | The number of external styles |
external-styles-size |
External styles size | The total size of external styles |
embedded-styles-count |
Embedded styles | The number of embedded styles |
embedded-styles-size |
Embedded styles size | The total size of embedded styles |
Id | Name | Description |
---|---|---|
externalise-style |
Externalise style | Avoid embedding style sheet into the HTML page as it would be transferred each time the page is requested |
minify-style |
Minify style | Reduce the size of the style sheet by minifying CSS code |
provide-print-style |
Provide print style | Optimise styles for printing |
reduce-styles-count |
Reduce styles count | Reduce the number of style sheets to reduce the number of requests |
reduce-style-size |
Reduce style size | Reduce the size of the style sheet: optimise CSS, remove unused styles, configure compression, ... |
Ecojourney is licensed under the GNU General Public License.