Single Page Tests


Single Page Tests (or SPTs for short) are synthetic speed tests for a URL of your choosing. The aim of SPTs is to provide fast feedback directly in your CLI or as part of a Continuous integration or deployment pipeline.

Each SPT is a totally independent report for a single page. There is no connection between SPTs and Sites within your account. You can use either concept independently.

Every SPT has a randomly generated public URL. You can create private tests (only viewable by logged-in members of your organisation) using the --private flag.

By default, SPTs expire after 1 year, you can specify expiry using the --expiresAt flag (Minimum 1 day, Maximum 2 years).

Create a page test

Required API Permission: Create Page Tests
tip

Tests can be run from different locations, using a range of device emulation and connection options. Experiment with the --location,--device and --connection flags.

1# This command will block until the test has been completed
2calibre test create "https://calibreapp.com" \
3  --location=Sydney \
4  --device=Desktop \
5  --connection=cable \
6  --private \
7  --waitForTest
1#!/usr/bin/env node
2
3import { Test } from 'calibre'
4
5const createTest = async () => {
6  // Required parameters
7  const url = 'https://calibreapp.com/docs/automation/cli'
8  const location = 'Sydney'
9  const device = 'Desktop'
10  const connection = 'cable'
11
12  // Optional parameters 👇
13  const expiresAt = new Date(Date.now() + 31556952000).toISOString() // Optional, Default = 1 year
14  const isPrivate = true // Default = false
15
16  const cookies = [
17    {
18      name: 'app.uid',
19      value: 'my-secret-tokens',
20      domain: 'calibreapp.com',
21      path: '/',
22      secure: true,
23      httpOnly: true
24    }
25  ]
26  const headers = [
27    {
28      name: 'User-Agent',
29      value: 'My Custom User Agent'
30    }
31  ]
32
33  // Create the test
34  const { uuid } = await Test.create({
35    url,
36    location,
37    device,
38    connection,
39    cookies,
40    headers,
41    isPrivate
42  })
43
44  console.log(`Test created, ID: ${uuid}`)
45
46  // Wait for the test to be run
47  const results = await Test.waitForTest(uuid)
48
49  // Output the formatted JSON response
50  console.log(JSON.stringify(results, null, 2))
51}
52
53createTest()
Example response
1✔ Test complete: https://calibreapp.com/tests/<uuid>/<share token>
2Performance report for your page. Learn more about Calibre’s Single Page Tests ↗
3
4### Test summary
5
6┌──────────────┬─────────────────────────────┬───────────────────┬──────────────────┬──────────────────┐
7│ Tested page  │ Test status                 │ Test location     │ Device emulation │ Connection speed │
8├──────────────┼─────────────────────────────┼───────────────────┼──────────────────┼──────────────────┤
9│ Inspect page │ ✅ Completed (Inspect test) │ Sydney, Australia │ Chrome Desktop   │ Cable            │
10└──────────────┴─────────────────────────────┴───────────────────┴──────────────────┴──────────────────┘
11
12### Web Vitals
13
14Web Vitals assessment passed. Learn more about Web Vitals ↗
15
16┌──────────────────────────┬─────────────────────────┬─────────────────────┐
17│ Largest Contentful Paint │ Cumulative Layout Shift │ Total Blocking Time │
18├──────────────────────────┼─────────────────────────┼─────────────────────┤
19│ 821ms (✅ Good)0.001 (✅ Good)         │ 83ms (✅ Good)20└──────────────────────────┴─────────────────────────┴─────────────────────┘
21
22### Assets transferred
23
24┌──────────────────────────────┬────────────┐
25│ Metric                       │ Value      │
26├──────────────────────────────┼────────────┤
27│ Total Font Transferred       │ 231 KB     │
28├──────────────────────────────┼────────────┤
29│ Total JavaScript Transferred │ 209 KB     │
30├──────────────────────────────┼────────────┤
31│ Total HTML Transferred       │ 33.1 KB    │
32├──────────────────────────────┼────────────┤
33│ Total Image Transferred      │ 27.5 KB    │
34├──────────────────────────────┼────────────┤
35│ Total JSON Transferred       │ 1.23 KB    │
36├──────────────────────────────┼────────────┤
37│ Total CSS Transferred        │ 1012 Bytes │
38├──────────────────────────────┼────────────┤
39│ Total Video Transferred      │ 0 Bytes    │
40├──────────────────────────────┼────────────┤
41│ Total Page Transferred       │ 502 KB     │
42└──────────────────────────────┴────────────┘
1{
2  "uuid": "dfa1a00",
3  "url": "https://calibreapp.com",
4  "formattedTestUrl": "https://calibreapp.com/tests/dfa1a00/9f0cb96",
5  "status": "completed",
6  "updatedAt": "2024-07-25T06:11:09Z",
7  "adBlockerIsEnabled": false,
8  "runtimeError": {},
9  "metrics": [
10    {
11      "name": "largest-contentful-paint",
12      "label": "Largest Contentful Paint",
13      "value": 1504
14    }
15		// More metrics here
16  ],
17  "device": {
18    "title": "Desktop"
19  },
20  "connection": {
21    "title": "Cable"
22  },
23  "location": {
24    "name": "Sydney, Australia",
25    "emoji": "🇦🇺"
26  }
27}

You can read CLI documentation for calibre test create here.

Test statuses

The test status field indicates the current state of the test:

StatusDescription
scheduledThe test has been received and is awaiting execution
runningThe test is currently being executed
processingResults have been received and are being finalised
completedThe test completed successfully
timeoutThe test did not complete within the 3 minute deadline
erroredThe test did not succeed due to an error

Create a page test, send results to a webhook

You can create page tests that automatically send JSON results to a webhook URL for processing and analysis. This is useful for integrating with other systems, such as CI/CD pipelines or custom integrations.

Webhooks fire when the test is completed, timeout or errored.

Webhooks can be verified using --webhookSecret flag or webhookSecret API parameter. The shared secret value will be signed using a HMAC cryptographic hash signature, which can be found on the Calibre-HMAC-SHA256-Signature HTTP header of the webhook request.

Required API Permission: Create Page Tests
1# Create a test then exit. Results will be sent to the webhook URL
2calibre test create "https://example.com" \
3  --location=Sydney \
4  --device=PageSpeedDesktop \
5  --private \
6  --webhookUrl=https://my-webhook-url.com \
7  --webhookSecret=my-secret
1#!/usr/bin/env node
2import { Test } from 'calibre'
3const createTest = async () => {
4  // Required parameters
5  const url = 'https://example.com'
6  const location = 'Sydney'
7  const device = 'PageSpeedDesktop'
8
9  // POST Single Page Test results to a Webhook URL for processing and analysis
10  const webhookUrl = 'https://my-webhook-url.com' // Optional
11  const webhookSecret = 'my-secret-webhook-token' // Optional
12
13  const isPrivate = true // Default = false
14
15  // Create the test
16  const { uuid } = await Test.create({
17    url,
18    location,
19    device,
20    webhookUrl,
21    webhookSecret,
22    isPrivate
23  })
24
25  console.log(`Test created, ID: ${uuid}`)
26}
27createTest()

View an existing test

Required API Permission: Read Page Tests
1# Pro tip: Add the --json flag for JSON output
2calibre test show <uuid>
1#!/usr/bin/env node
2
3import { Test } from 'calibre'
4
5const getTestData = async () => {
6  // Required
7  const uuid = 'dfa1a00'
8
9  // Fetch the test results
10  const results = await Test.getTestByUuid(uuid)
11
12  // Output the formatted JSON response
13  console.log(JSON.stringify(results, null, 2))
14}
15
16getTestData()
Example response
1✔ Test complete: https://calibreapp.com/tests/<uuid>/<share token>
2Performance report for your page. Learn more about Calibre’s Single Page Tests ↗
3
4### Test summary
5
6┌──────────────┬─────────────────────────────┬───────────────────┬──────────────────┬──────────────────┐
7│ Tested page  │ Test status                 │ Test location     │ Device emulation │ Connection speed │
8├──────────────┼─────────────────────────────┼───────────────────┼──────────────────┼──────────────────┤
9│ Inspect page │ ✅ Completed (Inspect test) │ Sydney, Australia │ Chrome Desktop   │ Cable            │
10└──────────────┴─────────────────────────────┴───────────────────┴──────────────────┴──────────────────┘
11
12### Web Vitals
13
14Web Vitals assessment passed. Learn more about Web Vitals ↗
15
16┌──────────────────────────┬─────────────────────────┬─────────────────────┐
17│ Largest Contentful Paint │ Cumulative Layout Shift │ Total Blocking Time │
18├──────────────────────────┼─────────────────────────┼─────────────────────┤
19│ 821ms (✅ Good)0.001 (✅ Good)         │ 83ms (✅ Good)20└──────────────────────────┴─────────────────────────┴─────────────────────┘
21
22### Assets transferred
23
24┌──────────────────────────────┬────────────┐
25│ Metric                       │ Value      │
26├──────────────────────────────┼────────────┤
27│ Total Font Transferred       │ 231 KB     │
28├──────────────────────────────┼────────────┤
29│ Total JavaScript Transferred │ 209 KB     │
30├──────────────────────────────┼────────────┤
31│ Total HTML Transferred       │ 33.1 KB    │
32├──────────────────────────────┼────────────┤
33│ Total Image Transferred      │ 27.5 KB    │
34├──────────────────────────────┼────────────┤
35│ Total JSON Transferred       │ 1.23 KB    │
36├──────────────────────────────┼────────────┤
37│ Total CSS Transferred        │ 1012 Bytes │
38├──────────────────────────────┼────────────┤
39│ Total Video Transferred      │ 0 Bytes    │
40├──────────────────────────────┼────────────┤
41│ Total Page Transferred       │ 502 KB     │
42└──────────────────────────────┴────────────┘
1{
2  "uuid": "dfa1a00",
3  "url": "https://calibreapp.com",
4  "formattedTestUrl": "https://calibreapp.com/tests/dfa1a00/9f0cb96",
5  "status": "completed",
6  "updatedAt": "2019-07-25T06:11:09Z",
7  "adBlockerIsEnabled": false,
8  "runtimeError": {},
9  "metrics": [
10    {
11      "name": "json_body_size_in_bytes",
12      "label": "Total JSON size in bytes",
13      "value": 15383
14    },
15    {
16      "name": "json_size_in_bytes",
17      "label": "Total JSON transferred",
18      "value": 11483
19    },
20    {
21      "name": "image_body_size_in_bytes",
22      "label": "Total Image size in bytes",
23      "value": 238120
24    },
25    {
26      "name": "image_size_in_bytes",
27      "label": "Total Image transferred",
28      "value": 77077
29    },
30    {
31      "name": "font_body_size_in_bytes",
32      "label": "Total Webfont size in bytes",
33      "value": 110125
34    },
35    {
36      "name": "font_size_in_bytes",
37      "label": "Total Webfont transferred",
38      "value": 111548
39    },
40    {
41      "name": "js_body_size_in_bytes",
42      "label": "Total JavaScript size in bytes",
43      "value": 2153354
44    },
45    {
46      "name": "js_size_in_bytes",
47      "label": "Total JavaScript Transferred",
48      "value": 516164
49    },
50    {
51      "name": "css_body_size_in_bytes",
52      "label": "Total CSS size in bytes",
53      "value": 80451
54    },
55    {
56      "name": "css_size_in_bytes",
57      "label": "Total CSS transferred",
58      "value": 17460
59    },
60    {
61      "name": "html_body_size_in_bytes",
62      "label": "Total HTML size in bytes",
63      "value": 24987
64    },
65    {
66      "name": "html_size_in_bytes",
67      "label": "Total HTML transferred",
68      "value": 10680
69    },
70    {
71      "name": "page_wait_timing",
72      "label": "Response time",
73      "value": 445
74    },
75    {
76      "name": "page_size_in_bytes",
77      "label": "Total Page transferred",
78      "value": 748537
79    },
80    {
81      "name": "page_body_size_in_bytes",
82      "label": "Total Page size in bytes",
83      "value": 2624120
84    },
85    {
86      "name": "asset_count",
87      "label": "Number of requests",
88      "value": 45
89    },
90    {
91      "name": "onload",
92      "label": "onLoad",
93      "value": 2572
94    },
95    {
96      "name": "oncontentload",
97      "label": "onContentLoad",
98      "value": 1671
99    },
100    {
101      "name": "lighthouse-seo-score",
102      "label": "Lighthouse SEO Score",
103      "value": 91
104    },
105    {
106      "name": "lighthouse-best-practices-score",
107      "label": "Lighthouse Best Practices Score",
108      "value": 93
109    },
110    {
111      "name": "lighthouse-accessibility-score",
112      "label": "Lighthouse Accessibility Score",
113      "value": 76
114    },
115    {
116      "name": "lighthouse-performance-score",
117      "label": "Lighthouse Performance Score",
118      "value": 75
119    },
120    {
121      "name": "visually_complete_85",
122      "label": "85% Visually Complete",
123      "value": 1872
124    },
125    {
126      "name": "visually_complete",
127      "label": "Visually Complete",
128      "value": 9288
129    },
130    {
131      "name": "consistently-interactive",
132      "label": "Time to Interactive",
133      "value": 7408
134    },
135    {
136      "name": "first-interactive",
137      "label": "First CPU Idle",
138      "value": 7408
139    },
140    {
141      "name": "time-to-first-byte",
142      "label": "Time to First Byte",
143      "value": 402
144    },
145    {
146      "name": "speed_index",
147      "label": "Speed Index",
148      "value": 1822
149    },
150    {
151      "name": "first-meaningful-paint",
152      "label": "First Meaningful Paint",
153      "value": 1504
154    },
155    {
156      "name": "first-contentful-paint",
157      "label": "First Contentful Paint",
158      "value": 1504
159    },
160    {
161      "name": "firstRender",
162      "label": "First Paint",
163      "value": 1504
164    }
165  ],
166  "device": {
167    "title": "Motorola Moto G4"
168  },
169  "connection": {
170    "title": "Emerging Markets 3G"
171  },
172  "location": {
173    "name": "North Virginia, USA",
174    "emoji": "🇺🇸"
175  }
176}

You can read CLI documentation for calibre test show here.

Retrieve test artifacts

Required API Permission: Read Page Tests

For each Single Page Test, Calibre stores the following information:

  • lighthouse.json
  • render progress screenshots
  • MP4 video render
  • HAR file (Request log)
  • all other metrics and data available through the interface

You can obtain Single Page Test artifacts with the CLI and the Node.js API. When using the CLi, files will be saved to a test-artifacts directory. The Node.js API will return a list of URLs where the files can be downloaded.

1calibre test download-artifacts <uuid>
1#!/usr/bin/env node
2
3import { Test } from 'calibre'
4
5const getTestArtifactUrls = async () => {
6  // Required
7  const uuid = 'dfa1a00'
8
9  // Fetch the test results
10  const results = await Test.fetchArtifacts(uuid)
11
12  // Output the formatted JSON response
13  console.log(JSON.stringify(results, null, 2))
14}
15
16getTestArtifactUrls()
Example response
1✔ Fetching test artifact URLs
2✔ Screenshot
3✔ MP4 Video Render
4✔ HAR
5✔ Lighthouse Report
6
7Saved artifacts to test-artifacts/dfa1a00
1{
2  "uuid": "dfa1a00",
3  "har": "https://calibre-screenshots-prod.s3.amazonaws.com/ef1ae88af9f4d643196a76cb62608449/b989bfe74fa26bed5063afb098e7ec4b.json.gz?X-Amz-Expires=3600&X-Amz-Date=20190725T061832Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAITFRYATLYAH7W3VQ/20190725/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=71cfa944d9c8243a55149091efb88913ef99b7d3373f44f7c4f28a69b0a1279d",
4  "lighthouse": "https://calibre-screenshots-prod.s3.amazonaws.com/ef1ae88af9f4d643196a76cb62608449/f92ce9906f4a55728df1ff112246e079.json.gz?X-Amz-Expires=3600&X-Amz-Date=20190725T061832Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAITFRYATLYAH7W3VQ/20190725/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=3cdb6e1fea6e00aa8c3e69d42b68aef0f1236550b858a8cc34bd7073275dd562",
5  "image": "https://calibre-screenshots-prod.s3.amazonaws.com/eb9dd012-7480-4368-b1f9-9e9f50e4f9f0/screenshot/screenshot.jpg",
6  "video": "https://calibre-screenshots-prod.s3.amazonaws.com/eb9dd012-7480-4368-b1f9-9e9f50e4f9f0/video-timeline/screencast.mp4"
7}

You can read CLI documentation for calibre test download-artifacts here.

List all tests

Required API Permission: Read Page Tests
1calibre test list
1#!/usr/bin/env node
2
3import { Test } from 'calibre'
4
5const listAllTests = async () => {
6  // Fetch the test list
7  const tests = await Test.getList()
8
9  // Output the formatted JSON response
10  console.log(JSON.stringify(tests, null, 2))
11}
12
13listAllTests()
Example response
1UUID    | URL                  | DEVICE           | CONNECTION    | LOCATION         | STATUS
2dfa1a00 | calibreapp.com/      | Motorola Moto G4 | Not Throttled | 🇺🇸  North Virginia | Completed 4:11pm 25-Jul-2019
3efa99de | calibreapp.com/      | Motorola Moto G4 | Not Throttled | 🇺🇸  North Virginia | Completed 4:36pm 25-Jul-2019
1[
2  {
3    "uuid": "dfa1a00",
4    "url": "https://calibreapp.com",
5    "formattedTestUrl": "https://calibreapp.com/tests/dfa1a00/9f0cb96",
6    "adBlockerIsEnabled": false,
7    "device": {
8      "title": "Motorola Moto G4"
9    },
10    "connection": {
11      "title": "Emerging Markets 3G"
12    },
13    "location": {
14      "emoji": "🇺🇸",
15      "shortName": "North Virginia"
16    },
17    "status": "completed",
18    "updatedAt": "2019-07-25T06:11:09Z"
19  },
20  {
21    "uuid": "efa99de",
22    "url": "https://calibreapp.com",
23    "formattedTestUrl": "https://calibreapp.com/tests/efa99de/39d030e",
24    "adBlockerIsEnabled": false,
25    "device": {
26      "title": "Motorola Moto G4"
27    },
28    "connection": {
29      "title": "Emerging Markets 3G"
30    },
31    "location": {
32      "emoji": "🇺🇸",
33      "shortName": "North Virginia"
34    },
35    "status": "completed",
36    "updatedAt": "2019-07-25T06:36:18Z"
37  }
38]

You can read CLI documentation for calibre test list here.