PURR (SEMrush puppeteer runner) is a devops-friendly tool for browser testing and monitoring.
The goal of this project is to have single set of browser checks, that could be used as tests, as canaries in CI/CD pipelines and as scenarios for production monitoring.
The tool uses puppeteer (https://pptr.dev/) to run standalone browsers (Chrome and Firefox are supported currently).
Checks results are stored as JSON reports, screenshots and traces.
PURR has three modes:
- CLI (mainly used in CI/CD pipelines)
- queue worker (scheduled monitoring)
- REST service (show results and expose internal metrics for prometheus)
Stores descriptions of every single check
Organizes checks into suites
Specifies check parameters, i.e. target host or cookie values
Define your schedules here
- Defaults from parameters.yml
- Defaults from check
- Params from env
- Explicitly specified params
docker build -f ./docker/Dockerfile . -t puppeteer-runner:latest
docker run -v "${PWD}/storage:/src/app/storage" puppeteer-runner:latest ./src/cli/cli.js check semrush-com
docker run -v "${PWD}/storage:/src/app/storage" puppeteer-runner:latest ./src/cli/cli.js suite semrush-suite
$ tree storage
storage
├── console_log
│ ├── console_semrush-com_0cedaca3-1153-47df-a616-55e21bf54635.log
│ └── console_semrush-com_ded5990f-7638-48e6-9d0e-77f8dba376fd.log
├── screenshots
│ ├── screenshot_semrush-com_0cedaca3-1153-47df-a616-55e21bf54635.png
│ └── screenshot_semrush-com_ded5990f-7638-48e6-9d0e-77f8dba376fd.png
└── traces
├── trace_semrush-com_0cedaca3-1153-47df-a616-55e21bf54635.json
└── trace_semrush-com_ded5990f-7638-48e6-9d0e-77f8dba376fd.json
Open trace in Chrome DevTools Timeline Viewer.
APP_IMAGE_NAME="puppeteer-runner" APP_IMAGE_VERSION="latest" NGINX_IMAGE_NAME="nginx" docker-compose up
docker-compose exec worker /app/src/cli.js schedule clean
docker-compose exec worker /app/src/cli.js schedule apply
docker-compose exec worker /app/src/cli.js schedule clean
Prometheus metrics
List of existing checks
Add check to queue
200: Returns check report 202: Returns id of created check job
- name: string Check name to run
- params: array Any check parameter
- wait: bool default: false Just return link for report when false
- view: string default: json options: json, pretty Output format
curl -X POST \
-d 'params[TARGET_SCHEMA]=http' \
-d 'params[TARGET_DOMAIN]=rc.example.com' \
http://purr.traefik.lcl/api/v1/checks/main-page?wait=true&view=pretty
Get report
- id: string Check report id
- view: string default: json options: json, pretty Output format
PURR translate scenatio steps described in ./data/checks.yml into method call of puppeteer.Page object You can check puppeteer reference documentation for up-to-date capabilities.
List of methods which were tested by the PURR dev team
- goto:
- '{{ TARGET_SCHEMA }}://{{ TARGET_DOMAIN }}/{{ TARGET_PAGE }}/'
- goto:
- '{{ TARGET_SCHEMA }}://{{ TARGET_DOMAIN }}/{{ TARGET_PAGE }}/'
- waitUntil: networkidle2
- waitForNavigation:
- waitUntil: domcontentloaded
- click:
- {{ CSS_OR_DOM_SELECTOR }}
- type:
- {{ CSS_OR_DOM_SELECTOR }}
- {{ STRING_TO_TYPE }}
- waitForSelector:
- {{ CSS_OR_DOM_SELECTOR }}
- waitForSelector:
- {{ CSS_OR_DOM_SELECTOR }}
- contains: {{ EXPECTED_TEXT }}
- setCookie:
- name: {{ COOKIE_NAME }}
value: {{ COOKIE_VALUE }}
domain: .{{ TARGET_DOMAIN.split('.').slice(-2).join('.') }}
Feel free to use YAML includes in your scenatios
.login_via_popup: &login_via_popup
- click:
- '[data-test="login"]'
- waitForSelector:
- '[data-test="email"]'
- type:
- '[data-test="email"]'
- {{USER_EMAIL}}
- type:
- '[data-test="password"]'
- {{USER_PASSWORD}}
- click:
- '[data-test="login-submit"]'
checks:
logged-user-dashboard:
parameters:
USER_PASSWORD: secret
steps:
- goto:
- {{ TARGET_URL }}
- waitUntil: networkidle2
<<: *login_via_popup
parameters:
USER_EMAIL: root@localhost
- waitForSelector:
- '[data-test="user-profile"]'
- contains: 'User Name:'
You can specify parameters in checks and suites yaml files under 'parameters' key
parameters:
TARGET_HOST: localhost
checks:
parameters:
USER_EMAIL: root@localhost
USER_PASSOWRD: secret
valid-password:
<<: *login_via_popup
invalid-password:
<<: *login_via_popup
parameters:
USER_PASSOWRD: invalid
IMPORTANT: It's expected that for convenient experience you will use vscode as an IDE with recommended extensions(configs are available in this repository).
make start-dev
make attach-dev
Run tests:
npm run test
We are using Jest testing framework.
You can mock module like that:
// If `manual` mock exist in dir `__mocks__` along module file, will be used
// automatically.
//
// Mocked module methods return `undefined`, fields return actual value.
jest.mock('../../config');
// Now `config` for all scripts will be `{ concurrency: 9 }`
jest.mock('../../config', () => ({ concurrency: 9 }));
Or like that:
const config = require('../../config');
config.concurrency = 1;
config.getWorkingPath = jest.fn().mockImplementation(() => {
return '/working/path';
});
Methods mock
\unmock
must be executed before module imports and in the
same scope.
Mocks state restoring after each test, but only when you did not used
jest.mock()