├── LICENSE
├── README.md
├── assets
└── images
│ ├── basic-test.png
│ ├── bulk-tests.png
│ ├── chrometracedata.png
│ ├── crux.png
│ ├── dryrun.png
│ ├── getresponsebody.png
│ ├── lighthouse-scores.png
│ ├── lighthouse.png
│ ├── recipe-banner.png
│ ├── screenshot-strip.jpg
│ ├── specs.png
│ ├── waterfall.png
│ ├── webpagetest-chrome-recorder.png
│ └── webvitals.png
├── bulk-tests.js
├── connectivity-custom.js
├── dryrun.js
├── getChromeTraceData.js
├── getResponseBody.js
├── lighthouse.js
├── mobile-device.js
├── multistep.js
├── network-and-cpu-throttling.js
├── screenshot-strip.js
├── slow-network.js
├── testspecs.js
├── third-party-domain-blocked.js
├── waterfall-image.js
├── webpagetest-chrome-recorder.js
├── webvitals-crux.js
└── webvitals.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 WebPageTest
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | WebPageTest API Recipes
4 |
5 |
6 | 👩🍳 A collection of useful recipes for the WebPageTest API
7 |
8 | 🍔 What's your favorite recipe?
9 |
10 | ## 📖Table Of Contents
11 |
12 | - [Emulate a slow network](#emulate-a-slow-network)
13 | - [Emulate a slow network and CPU throttling](#emulate-network-&-cputhrottle)
14 | - [Emulate a custom connectivity (Bandwidth, Latency, PacketLossRate)](#emulate-a-custom-connectivity)
15 | - [Emulate a test on mobile device](#Emulate-a-test-on-mobile-device)
16 | - [Retrieve your Core Web Vitals](#retrieve-your-core-web-vitals)
17 | - [Retrieve your Core Web Vitals + CrUX data for the tested URL](#retrieve-your-core-web-vitals-+-crux)
18 | - [Run a test with a third-party domain blocked](#run-a-test-with-a-third-party-domain-blocked)
19 | - [Run a test and get the filmstrip screenshots](#run-a-test-and-get-the-filmstrip-screenshots)
20 | - [Run a test and generate a lighthouse report](#run-a-test-and-generate-a-lighthouse-report)
21 | - [Run a multi-step test with scripting](#run-a-multi-step-test-with-scripting)
22 | - [Run a test and generate a waterfall image](#run-a-test-and-generate-a-waterfall-image)
23 | - [Run tests on multiple URLs](#run-tests-on-multiple-urls)
24 | - [Create a URL endpoint](#create-a-url-endpoint)
25 | - [Run a test and check a budget using testspecs](#run-a-test-and-check-a-budget-using-testspecs)
26 | - [Run a test using webpagetest chrome recorder](#run-a-test-using-webpagetest-chrome-recorder)
27 | - [Retrieving chrome trace data](#retrieving-chrome-trace-data)
28 | - [Retrieving response body](#retrieving-response-body)
29 |
30 | Emulate a slow network
31 |
32 | ```js
33 | import WebPageTest from "webpagetest";
34 |
35 | const wptServer = "https://www.webpagetest.org";
36 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
37 |
38 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
39 |
40 | // Simulated network throttling (Slow 3G)
41 | let options = {
42 | location: "Dulles:Chrome", //mandatory with connectivity
43 | connectivity: "3G",
44 | };
45 |
46 | // Run the test
47 | wpt.runTest(testURL, options, (err, result) => {
48 | if (result) {
49 | console.log(result);
50 | } else {
51 | console.log(err);
52 | }
53 | });
54 |
55 | ```
56 | 
57 |
58 | [Source](slow-network.js)
59 |
60 | Emulate a slow network and CPU throttling
61 |
62 | ```js
63 | import WebPageTest from "webpagetest";
64 |
65 | const wptServer = "https://www.webpagetest.org";
66 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
67 |
68 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
69 |
70 | // Simulated network & cpu throttling
71 | let options = {
72 | location: "Dulles:Chrome",
73 | connectivity: "3G",
74 | throttleCPU: 5,
75 | };
76 |
77 | // Run the test
78 | wpt.runTest(testURL, options, (err, result) => {
79 | if (result) {
80 | console.log(result);
81 | } else {
82 | console.log(err);
83 | }
84 | });
85 |
86 |
87 | ```
88 |
89 | [Source](network-and-cpu-throttling.js)
90 |
91 | Emulate a custom connectivity (Bandwidth, Latency, PacketLossRate)
92 |
93 | ```js
94 | import WebPageTest from "webpagetest";
95 |
96 | const wptServer = "https://www.webpagetest.org";
97 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
98 |
99 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
100 |
101 | // Simulated custom connectivity options (custom)
102 | let options = {
103 | connectivity: "custom",
104 | location: "ec2-us-east-1:Chrome",
105 | label: "custom connectivity",
106 | bandwidthDown: 1000,
107 | bandwidthUp: 1000,
108 | latency: 5,
109 | packetLossRate: 5,
110 | };
111 |
112 | // Run the test
113 | wpt.runTest(testURL, options, (err, result) => {
114 | if (result) {
115 | console.log(result);
116 | } else {
117 | console.log(err);
118 | }
119 | });
120 |
121 | ```
122 |
123 | [Source](connectivity-custom.js)
124 |
125 | Emulate a test on mobile device
126 |
127 | ```js
128 | import WebPageTest from "webpagetest";
129 |
130 | const wptServer = "https://www.webpagetest.org";
131 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
132 |
133 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
134 |
135 | let options = {
136 | location: "ec2-us-east-1:Chrome",
137 | label: "emulate mobile device",
138 | firstViewOnly: true,
139 | emulateMobile: true,
140 | device: "Nexus5", // optional (default: MotoG4)
141 | };
142 |
143 | //Supported devices: https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/mobile_devices.ini
144 |
145 | // Run the test
146 | wpt.runTest(testURL, options, (err, result) => {
147 | if (result) {
148 | console.log(result);
149 | } else {
150 | console.log(err);
151 | }
152 | });
153 |
154 | ```
155 |
156 | [Source](mobile-device.js)
157 |
158 | Retrieve your Core Web Vitals
159 |
160 | ```js
161 | import WebPageTest from "webpagetest";
162 |
163 | const wptServer = "https://www.webpagetest.org";
164 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
165 |
166 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
167 |
168 | let options = {
169 | firstViewOnly: true,
170 | location: "Dulles:Chrome",
171 | pollResults: 60,
172 | timeout: 240,
173 | };
174 |
175 | wpt.runTest(testURL, options, (err, result) => {
176 | if (result) {
177 | console.log({
178 | CumulativeLayoutShift: result.data.average.firstView["chromeUserTiming.CumulativeLayoutShift"],
179 | LargestContentfulPaint: result.data.average.firstView["chromeUserTiming.LargestContentfulPaint"],
180 | TotalBlockingTime: result.data.average.firstView["TotalBlockingTime"],
181 | });
182 | } else {
183 | console.log(err);
184 | }
185 | });
186 |
187 |
188 | ```
189 | 
190 |
191 | [Source](webvitals.js)
192 |
193 |
194 | Retrieve your Core Web Vitals + CrUX data for the tested URL
195 |
196 | ```js
197 | import WebPageTest from "webpagetest";
198 |
199 | const wptServer = "https://www.webpagetest.org";
200 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
201 |
202 | let testURL = "https://www.webpagetest.org/"; //Your URL here
203 |
204 | let options = {
205 | firstViewOnly: true,
206 | location: "Dulles:Chrome",
207 | pollResults: 60,
208 | timeout: 240,
209 | };
210 |
211 | wpt.runTest(testURL, options, (err, result) => {
212 | if (result) {
213 | console.log("<-------------Core Web Vitals------------->");
214 | console.log({
215 | CumulativeLayoutShift: result.data.average.firstView["chromeUserTiming.CumulativeLayoutShift"],
216 | LargestContentfulPaint: result.data.average.firstView["chromeUserTiming.LargestContentfulPaint"],
217 | TotalBlockingTime: result.data.average.firstView["TotalBlockingTime"],
218 | });
219 |
220 | if (result.data.median.firstView.CrUX !== undefined) {
221 | console.log("<----------------Crux Data---------------->");
222 | console.log(result.data.median.firstView.CrUX);
223 | } else {
224 | console.log("No CrUX Data Found");
225 | }
226 | } else {
227 | console.log(err);
228 | }
229 | });
230 |
231 | ```
232 |
233 | 
234 |
235 | [Source](webvitals-crux.js)
236 |
237 | Run a test with a third-party domain blocked
238 |
239 | ```js
240 | import WebPageTest from "webpagetest";
241 |
242 | const wptServer = "https://www.webpagetest.org";
243 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
244 |
245 | let testURL = "https://theverge.com"; //Your URL here
246 |
247 | // URL's must be seprated by spaces (space-delimited)
248 | let options = {
249 | block:
250 | "https://pagead2.googlesyndication.com https://creativecdn.com https://www.googletagmanager.com https://cdn.krxd.net https://adservice.google.com https://cdn.concert.io https://z.moatads.com https://cdn.permutive.com",
251 | };
252 |
253 | // Run the test
254 | wpt.runTest(testURL, options, (err, result) => {
255 | if (result) {
256 | console.log(result);
257 | } else {
258 | console.log(err);
259 | }
260 | });
261 |
262 | ```
263 |
264 | [Source](third-party-domain-blocked.js)
265 |
266 | Run a test and get the filmstrip screenshots
267 |
268 | ```js
269 | import WebPageTest from "webpagetest";
270 | import fs from "fs";
271 | import axios from "axios";
272 |
273 | const wptServer = "https://www.webpagetest.org";
274 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
275 |
276 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
277 |
278 | let options = {
279 | firstViewOnly: true,
280 | location: "Dulles:Chrome",
281 | connectivity: "4G",
282 | pollResults: 60, //keep polling for results after test is scheduled
283 | };
284 |
285 | wpt.runTest(testURL, options, (err, result) => {
286 | if (result) {
287 | result.data.median.firstView.videoFrames.forEach((item, index) => {
288 | axios({
289 | method: "get",
290 | url: item.image,
291 | responseType: "stream",
292 | }).then(function (response) {
293 | response.data.pipe(fs.createWriteStream(`screenshot-${index}.png`));
294 | });
295 | });
296 | } else {
297 | console.log(err);
298 | }
299 | });
300 |
301 | ```
302 | 
303 |
304 | [Source](screenshot-strip.js)
305 |
306 | Run a test and generate a lighthouse report
307 |
308 | ```js
309 | import WebPageTest from "webpagetest";
310 |
311 | const wptServer = "https://www.webpagetest.org";
312 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
313 |
314 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
315 |
316 | let options = {
317 | pollResults: 60,
318 | timeout: 240,
319 | lighthouse: 1, // This parameter will generate both WPT results and Lighthouse report
320 | };
321 |
322 | // Run the test
323 | wpt.runTest(testURL, options, (err, result) => {
324 | if (result) {
325 | console.log(`\n
326 | Lighthouse scores:
327 | Performance: ${result.data.lighthouse.categories.performance.score * 100},
328 | Accessibility: ${result.data.lighthouse.categories.accessibility.score * 100},
329 | Best Practices: ${result.data.lighthouse.categories['best-practices'].score * 100},
330 | SEO: ${result.data.lighthouse.categories.seo.score * 100},
331 | PWA: ${result.data.lighthouse.categories.pwa.score * 100}
332 |
333 | Lighthouse report: https://www.webpagetest.org/lighthouse.php?test=${result.data.id}
334 | Full WebPageTest results: ${result.data.summary}
335 | `);
336 | } else {
337 | console.log(err);
338 | }
339 | });
340 |
341 | ```
342 | 
343 |
344 | 
345 |
346 | [Source](lighthouse.js)
347 |
348 | Run a multi-step test with scripting
349 |
350 | ```js
351 | import WebPageTest from "webpagetest";
352 |
353 | const wptServer = "https://www.webpagetest.org";
354 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
355 |
356 | let options = {
357 | pollResults: 60,
358 | firstViewOnly: true, //Skips the Repeat View test
359 | };
360 |
361 | const script = wpt.scriptToString([
362 | { logData: 0 },
363 | { navigate: "http://foo.com/login" },
364 | { logData: 1 },
365 | { setValue: ["name=username", "johndoe"] },
366 | { setValue: ["name=password", "12345"] },
367 | { submitForm: "action=http://foo.com/main" },
368 | "waitForComplete",
369 | ]);
370 |
371 | // Run the test
372 | wpt.runTest(script, options, (err, result) => {
373 | if (result) {
374 | console.log(result);
375 | } else {
376 | console.log(err);
377 | }
378 | });
379 |
380 | ```
381 |
382 | Visit [Scripting Docs](https://docs.webpagetest.org/scripting/) for more information
383 |
384 | [Source](multistep.js)
385 |
386 |
387 | Run a test and generate a waterfall image
388 |
389 | ```js
390 | import WebPageTest from "webpagetest";
391 | import fs from "fs";
392 | import axios from "axios";
393 |
394 | const wptServer = "https://www.webpagetest.org";
395 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
396 |
397 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
398 |
399 | let options = {
400 | firstViewOnly: true,
401 | location: "Dulles:Chrome",
402 | connectivity: "4G",
403 | pollResults: 60, //keep polling for results after test is scheduled
404 | };
405 |
406 | wpt.runTest(testURL, options, (err, result) => {
407 | if (result) {
408 | let imgurl = result.data.median.firstView.images.waterfall;
409 |
410 | axios({
411 | method: "get",
412 | url: imgurl,
413 | responseType: "stream",
414 | }).then(function (response) {
415 | response.data.pipe(fs.createWriteStream("waterfall.png"));
416 | });
417 | } else {
418 | console.log(err);
419 | }
420 | });
421 |
422 | ```
423 | 
424 |
425 | [Source](waterfall-image.js)
426 |
427 | Run tests on multiple URLs
428 |
429 | ```js
430 | import WebPageTest from "webpagetest";
431 |
432 | const wpt = new WebPageTest("www.webpagetest.org", "YOUR_API_KEY");
433 | const finalResults = [];
434 |
435 | // Your list of URLs to test
436 | let urls = [
437 | "https://www.webpagetest.org/",
438 | "https://www.product.webpagetest.org/api",
439 | "https://docs.webpagetest.org/api/",
440 | "https://blog.webpagetest.org/",
441 | "https://www.webpagetest.org/about",
442 | ];
443 |
444 | let options = {
445 | firstViewOnly: true,
446 | location: "Dulles:Chrome",
447 | connectivity: "4G",
448 | pollResults: 60,
449 | timeout: 240,
450 | };
451 |
452 | const runTest = (wpt, url, options) => {
453 | return new Promise((resolve, reject) => {
454 | console.log(`Submitting test for ${url}...`);
455 | wpt.runTest(url, options, async (err, result) => {
456 | try {
457 | if (result) {
458 | return resolve(result);
459 | } else {
460 | return reject(err);
461 | }
462 | } catch (e) {
463 | console.info(e);
464 | }
465 | });
466 | });
467 | };
468 |
469 | (async function () {
470 | Promise.all(
471 | urls.map(async (url) => {
472 | try {
473 | await runTest(wpt, url, options).then(async (result) => {
474 | if (result.data) {
475 | let median = result.data.median.firstView;
476 | //Pushing the data into the Array
477 | finalResults.push({
478 | id: result.data.id,
479 | url: result.data.url,
480 | cls: median["chromeUserTiming.CumulativeLayoutShift"],
481 | lcp: median["chromeUserTiming.LargestContentfulPaint"],
482 | tbt: median["TotalBlockingTime"],
483 | });
484 | }
485 | });
486 | } catch (e) {
487 | console.error(e);
488 | }
489 | })
490 | ).then(() => {
491 | console.info(finalResults);
492 | });
493 | })();
494 |
495 | ```
496 | 
497 |
498 | [Source](bulk-tests.js)
499 |
500 | Create a URL endpoint
501 |
502 | ```js
503 | import WebPageTest from "webpagetest";
504 |
505 | const wptServer = "https://www.webpagetest.org";
506 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
507 |
508 | let options = {
509 | dryRun: true, // outputs the api endpoint
510 | };
511 |
512 | // multistep script
513 | const script = wpt.scriptToString([
514 | { navigate: 'https://timkadlec.com/' },
515 | { execAndWait: 'document.querySelector("#nav > ul > li:nth-child(2) > a").click();' },
516 | { execAndWait: 'document.querySelector("#nav > ul > li:nth-child(3) > a").click();' },
517 | { execAndWait: 'document.querySelector("#nav > ul > li:nth-child(4) > a").click();' },
518 | ]);
519 |
520 | // fire up the runtest function with a script or a url
521 | wpt.runTest(script, options, (err, result) => {
522 | if (result) {
523 | console.log(result);
524 | } else {
525 | console.log(err);
526 | }
527 | });
528 |
529 |
530 | ```
531 | 
532 |
533 | [Source](dryrun.js)
534 |
535 | Run a test and check a budget using testspecs
536 |
537 | ```js
538 | import WebPageTest from "webpagetest";
539 |
540 | const wptServer = "https://www.webpagetest.org";
541 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
542 |
543 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
544 |
545 | let options = {
546 | firstViewOnly: true,
547 | location: "Dulles:Chrome",
548 | pollResults: 60,
549 | timeout: 240,
550 | // Set you budget specs here
551 | specs: {
552 | average: {
553 | firstView: {
554 | "chromeUserTiming.CumulativeLayoutShift": 0.1,
555 | "chromeUserTiming.LargestContentfulPaint": 2500,
556 | firstContentfulPaint: 2000,
557 | TotalBlockingTime: 0.1,
558 | },
559 | },
560 | },
561 | };
562 |
563 | wpt.runTest(testURL, options, (err, result) => {
564 | if (result) {
565 | console.log(`Your results are here for test ID:- ${result.testId}`);
566 | } else {
567 | console.log(err);
568 | }
569 | });
570 |
571 | ```
572 | 
573 |
574 | Check [Testspecs](https://github.com/WebPageTest/webpagetest-api/wiki/Test-Specs) for more details on setting a budget
575 |
576 | [Source](testspecs.js)
577 |
578 | Run a test using webpagetest chrome recorder
579 |
580 | ```js
581 | import WebPageTest from "webpagetest";
582 | import { WPTStringifyChromeRecording } from "webpagetest-chrome-recorder";
583 |
584 | //Recording generated using chrome recorder
585 | const recordingContent = {
586 | title: "Webpagetest Chrome Recorder",
587 | steps: [
588 | {
589 | type: "setViewport",
590 | width: 1263,
591 | height: 600,
592 | deviceScaleFactor: 1,
593 | isMobile: false,
594 | hasTouch: false,
595 | isLandscape: false,
596 | },
597 | {
598 | type: "navigate",
599 | url: "https://blog.webpagetest.org/",
600 | assertedEvents: [
601 | {
602 | type: "navigation",
603 | url: "https://blog.webpagetest.org/",
604 | title: "WebPageTest Blog",
605 | },
606 | ],
607 | },
608 | {
609 | type: "click",
610 | target: "main",
611 | selectors: [["header li:nth-of-type(2) > a"]],
612 | offsetY: 27.802078247070312,
613 | offsetX: 26.427078247070312,
614 | assertedEvents: [
615 | {
616 | type: "navigation",
617 | url: "https://blog.webpagetest.org/categories/webpagetest-news/",
618 | title: "",
619 | },
620 | ],
621 | },
622 | ],
623 | };
624 |
625 | //Converting json recording to webpagetest script
626 | const script = await WPTStringifyChromeRecording(recordingContent);
627 | console.log("Stringified Webpagetest Recorder Script: \n\n" + script + "\n");
628 |
629 | // Initializing webpagetest
630 | const wptServer = "https://www.webpagetest.org";
631 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
632 |
633 | let options = {
634 | firstViewOnly: true,
635 | label: recordingContent.title,
636 | };
637 |
638 | console.log("Webpagetest Custom Script Test Result: \n");
639 |
640 | // Run the test using webpagetest script
641 | wpt.runTest(script, options, (err, result) => {
642 | if (result) {
643 | console.log(result);
644 | } else {
645 | console.log(err);
646 | }
647 | });
648 |
649 | ```
650 | 
651 |
652 | Check [Webpagetest Chrome Recorder](https://github.com/WebPageTest/Recorder-To-WPT-Script) for more details
653 |
654 | [Source](webpagetest-chrome-recorder.js)
655 |
656 | Retrieving chrome trace data
657 |
658 | ```js
659 | import WebPageTest from "webpagetest";
660 |
661 | const wptServer = "https://www.webpagetest.org";
662 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
663 |
664 | let testId = "YOUR_TEST_ID"; //Your URL here
665 |
666 | // Retrieving Chrome Trace Data
667 | wpt.getChromeTraceData(testId, (err, result) => {
668 | if (result) {
669 | console.log(result);
670 | } else {
671 | console.log(err);
672 | }
673 | });
674 |
675 | ```
676 | 
677 |
678 | [Source](getChromeTraceData.js)
679 |
680 | Retrieving Response Body
681 |
682 | ```js
683 | import WebPageTest from "webpagetest";
684 |
685 | const wptServer = "https://www.webpagetest.org";
686 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
687 |
688 | let testID = "YOUR_TEST_ID";
689 |
690 | let options = {
691 | run: 1, // the run from which you'd want to fetch the response body
692 | request: 2, // the request number same as waterfall
693 | cached: 0, // check for the repeat view
694 | };
695 |
696 | // Retrieving response body (Make sure you've enabled the save response body on this test)
697 | wpt.getResponseBody(testID, options, (err, result) => {
698 | if (result) {
699 | console.log(result);
700 | } else {
701 | console.log(err);
702 | }
703 | });
704 |
705 | ```
706 | 
707 |
708 | [Source](getResponseBody.js)
--------------------------------------------------------------------------------
/assets/images/basic-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/basic-test.png
--------------------------------------------------------------------------------
/assets/images/bulk-tests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/bulk-tests.png
--------------------------------------------------------------------------------
/assets/images/chrometracedata.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/chrometracedata.png
--------------------------------------------------------------------------------
/assets/images/crux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/crux.png
--------------------------------------------------------------------------------
/assets/images/dryrun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/dryrun.png
--------------------------------------------------------------------------------
/assets/images/getresponsebody.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/getresponsebody.png
--------------------------------------------------------------------------------
/assets/images/lighthouse-scores.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/lighthouse-scores.png
--------------------------------------------------------------------------------
/assets/images/lighthouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/lighthouse.png
--------------------------------------------------------------------------------
/assets/images/recipe-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/recipe-banner.png
--------------------------------------------------------------------------------
/assets/images/screenshot-strip.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/screenshot-strip.jpg
--------------------------------------------------------------------------------
/assets/images/specs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/specs.png
--------------------------------------------------------------------------------
/assets/images/waterfall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/waterfall.png
--------------------------------------------------------------------------------
/assets/images/webpagetest-chrome-recorder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/webpagetest-chrome-recorder.png
--------------------------------------------------------------------------------
/assets/images/webvitals.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catchpoint/WebPageTest.api-recipes/8fe8160fbadbf316427f9891a4f9a896edbc2dda/assets/images/webvitals.png
--------------------------------------------------------------------------------
/bulk-tests.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | const finalResults = [];
7 |
8 | // Your list of URLs to test
9 | let urls = [
10 | "https://www.webpagetest.org/",
11 | "https://www.product.webpagetest.org/api",
12 | "https://docs.webpagetest.org/api/",
13 | "https://blog.webpagetest.org/",
14 | "https://www.webpagetest.org/about",
15 | ];
16 |
17 | let options = {
18 | firstViewOnly: true,
19 | location: "Dulles:Chrome",
20 | connectivity: "4G",
21 | pollResults: 5,
22 | timeout: 240,
23 | };
24 |
25 | const runTest = (wpt, url, options) => {
26 | return new Promise((resolve, reject) => {
27 | console.log(`Submitting test for ${url}...`);
28 | wpt.runTest(url, options, async (err, result) => {
29 | try {
30 | if (result) {
31 | return resolve(result);
32 | } else {
33 | return reject(err);
34 | }
35 | } catch (e) {
36 | console.info(e);
37 | }
38 | });
39 | });
40 | };
41 |
42 | (async function () {
43 | Promise.all(
44 | urls.map(async (url) => {
45 | try {
46 | await runTest(wpt, url, options).then(async (result) => {
47 | if (result.data) {
48 | let median = result.data.median.firstView;
49 | //Pushing the data into the Array
50 | finalResults.push({
51 | id: result.data.id,
52 | url: result.data.url,
53 | cls: median["chromeUserTiming.CumulativeLayoutShift"],
54 | lcp: median["chromeUserTiming.LargestContentfulPaint"],
55 | tbt: median["TotalBlockingTime"],
56 | });
57 | }
58 | });
59 | } catch (e) {
60 | console.error(e);
61 | }
62 | })
63 | ).then(() => {
64 | console.info(finalResults);
65 | });
66 | })();
67 |
--------------------------------------------------------------------------------
/connectivity-custom.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | // Simulated custom connectivity options (custom)
9 | let options = {
10 | connectivity: "custom",
11 | location: "ec2-us-east-1:Chrome",
12 | label: "custom connectivity",
13 | bandwidthDown: 1000,
14 | bandwidthUp: 1000,
15 | latency: 5,
16 | packetLossRate: 5,
17 | };
18 |
19 | // Run the test
20 | wpt.runTest(testURL, options, (err, result) => {
21 | if (result) {
22 | console.log(result);
23 | } else {
24 | console.log(err);
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/dryrun.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let options = {
7 | dryRun: true, // outputs the api endpoint
8 | };
9 |
10 | // multistep script
11 | const script = wpt.scriptToString([
12 | { navigate: "https://timkadlec.com/" },
13 | { execAndWait: 'document.querySelector("#nav > ul > li:nth-child(2) > a").click();' },
14 | { execAndWait: 'document.querySelector("#nav > ul > li:nth-child(3) > a").click();' },
15 | { execAndWait: 'document.querySelector("#nav > ul > li:nth-child(4) > a").click();' },
16 | ]);
17 |
18 | // fire up the runtest function with a script or a url
19 | wpt.runTest(script, options, (err, result) => {
20 | if (result) {
21 | console.log(result);
22 | } else {
23 | console.log(err);
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/getChromeTraceData.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testId = "YOUR_TEST_ID"; //Your URL here
7 |
8 | // Retrieving Chrome Trace Data
9 | wpt.getChromeTraceData(testId, (err, result) => {
10 | if (result) {
11 | console.log(result);
12 | } else {
13 | console.log(err);
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/getResponseBody.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testID = "YOUR_TEST_ID";
7 |
8 | let options = {
9 | run: 1, // the run from which you'd want to fetch the response body
10 | request: 2, // the request number same as waterfall
11 | cached: 1, // check for the repeat view
12 | };
13 |
14 | // Retrieving response body (Make sure you've enabled the save response body on this test)
15 | wpt.getResponseBody(testID, options, (err, result) => {
16 | if (result) {
17 | console.log(result);
18 | } else {
19 | console.log(err);
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/lighthouse.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | let options = {
9 | pollResults: 5,
10 | timeout: 240,
11 | lighthouse: 1, // This parameter will generate both WPT results and Lighthouse report
12 | };
13 |
14 | // Run the test
15 | wpt.runTest(testURL, options, (err, result) => {
16 | if (result) {
17 | console.log(`\n
18 | Lighthouse scores:
19 | Performance: ${result.data.lighthouse.categories.performance.score * 100},
20 | Accessibility: ${result.data.lighthouse.categories.accessibility.score * 100},
21 | Best Practices: ${result.data.lighthouse.categories["best-practices"].score * 100},
22 | SEO: ${result.data.lighthouse.categories.seo.score * 100},
23 | PWA: ${result.data.lighthouse.categories.pwa.score * 100}
24 |
25 | Lighthouse report: https://www.webpagetest.org/lighthouse.php?test=${result.data.id}
26 | Full WebPageTest results: ${result.data.summary}
27 | `);
28 | } else {
29 | console.log(err);
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/mobile-device.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | let options = {
9 | location: "ec2-us-east-1:Chrome",
10 | label: "emulate mobile device",
11 | firstViewOnly: true,
12 | emulateMobile: true,
13 | device: "Nexus5", // optional (default: MotoG4)
14 | };
15 |
16 | //List of support devices https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/mobile_devices.ini
17 |
18 | // Run the test
19 | wpt.runTest(testURL, options, (err, result) => {
20 | if (result) {
21 | console.log(result);
22 | } else {
23 | console.log(err);
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/multistep.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let options = {
7 | pollResults: 5,
8 | firstViewOnly: true, //Skips the Repeat View test
9 | };
10 |
11 | const script = wpt.scriptToString([
12 | { logData: 0 },
13 | { navigate: "http://foo.com/login" },
14 | { logData: 1 },
15 | { setValue: ["name=username", "johndoe"] },
16 | { setValue: ["name=password", "12345"] },
17 | { submitForm: "action=http://foo.com/main" },
18 | "waitForComplete",
19 | ]);
20 |
21 | // Run the test
22 | wpt.runTest(script, options, (err, result) => {
23 | if (result) {
24 | console.log(result);
25 | } else {
26 | console.log(err);
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/network-and-cpu-throttling.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | // Simulated network & cpu throttling
9 | let options = {
10 | location: "Dulles:Chrome",
11 | connectivity: "3G",
12 | throttleCPU: 5,
13 | };
14 |
15 | // Run the test
16 | wpt.runTest(testURL, options, (err, result) => {
17 | if (result) {
18 | console.log(result);
19 | } else {
20 | console.log(err);
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/screenshot-strip.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 | import fs from "fs";
3 | import axios from "axios";
4 |
5 | const wptServer = "https://www.webpagetest.org";
6 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
7 |
8 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
9 |
10 | let options = {
11 | firstViewOnly: true,
12 | location: "Dulles:Chrome",
13 | connectivity: "4G",
14 | pollResults: 5, //keep polling for results after test is scheduled
15 | };
16 |
17 | wpt.runTest(testURL, options, (err, result) => {
18 | if (result) {
19 | result.data.median.firstView.videoFrames.forEach((item, index) => {
20 | axios({
21 | method: "get",
22 | url: item.image,
23 | responseType: "stream",
24 | }).then(function (response) {
25 | response.data.pipe(fs.createWriteStream(`screenshot-${index}.png`));
26 | });
27 | });
28 | } else {
29 | console.log(err);
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/slow-network.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | // Simulated network throttling (Slow 3G)
9 | let options = {
10 | location: "Dulles:Chrome", //mandatory with connectivity
11 | connectivity: "3G",
12 | };
13 |
14 | // Run the test
15 | wpt.runTest(testURL, options, (err, result) => {
16 | if (result) {
17 | console.log(result);
18 | } else {
19 | console.log(err);
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/testspecs.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | let options = {
9 | firstViewOnly: true,
10 | location: "Dulles:Chrome",
11 | pollResults: 5,
12 | timeout: 240,
13 | // Set you budget specs here
14 | specs: {
15 | average: {
16 | firstView: {
17 | "chromeUserTiming.CumulativeLayoutShift": 0.1,
18 | "chromeUserTiming.LargestContentfulPaint": 2500,
19 | firstContentfulPaint: 2000,
20 | TotalBlockingTime: 0.1,
21 | },
22 | },
23 | },
24 | };
25 |
26 | wpt.runTest(testURL, options, (err, result) => {
27 | if (result) {
28 | console.log(`Your results are here for test ID:- ${result.testId}`);
29 | } else {
30 | console.log(err);
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/third-party-domain-blocked.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://theverge.com"; //Your URL here
7 |
8 | // URL's must be seprated by spaces (space-delimited)
9 | let options = {
10 | block:
11 | "https://pagead2.googlesyndication.com https://creativecdn.com https://www.googletagmanager.com https://cdn.krxd.net https://adservice.google.com https://cdn.concert.io https://z.moatads.com https://cdn.permutive.com",
12 | };
13 |
14 | // Run the test
15 | wpt.runTest(testURL, options, (err, result) => {
16 | if (result) {
17 | console.log(result);
18 | } else {
19 | console.log(err);
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/waterfall-image.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 | import fs from "fs";
3 | import axios from "axios";
4 |
5 | const wptServer = "https://www.webpagetest.org";
6 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
7 |
8 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
9 |
10 | let options = {
11 | firstViewOnly: true,
12 | location: "Dulles:Chrome",
13 | connectivity: "4G",
14 | pollResults: 5, //keep polling for results after test is scheduled
15 | };
16 |
17 | wpt.runTest(testURL, options, (err, result) => {
18 | if (result) {
19 | let imgurl = result.data.median.firstView.images.waterfall;
20 |
21 | axios({
22 | method: "get",
23 | url: imgurl,
24 | responseType: "stream",
25 | }).then(function (response) {
26 | response.data.pipe(fs.createWriteStream("waterfall.png"));
27 | });
28 | } else {
29 | console.log(err);
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/webpagetest-chrome-recorder.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 | import { WPTStringifyChromeRecording } from "webpagetest-chrome-recorder";
3 |
4 | //Recording generated using chrome recorder
5 | const recordingContent = {
6 | title: "Webpagetest Chrome Recorder",
7 | steps: [
8 | {
9 | type: "setViewport",
10 | width: 1263,
11 | height: 600,
12 | deviceScaleFactor: 1,
13 | isMobile: false,
14 | hasTouch: false,
15 | isLandscape: false,
16 | },
17 | {
18 | type: "navigate",
19 | url: "https://blog.webpagetest.org/",
20 | assertedEvents: [
21 | {
22 | type: "navigation",
23 | url: "https://blog.webpagetest.org/",
24 | title: "WebPageTest Blog",
25 | },
26 | ],
27 | },
28 | {
29 | type: "click",
30 | target: "main",
31 | selectors: [["header li:nth-of-type(2) > a"]],
32 | offsetY: 27.802078247070312,
33 | offsetX: 26.427078247070312,
34 | assertedEvents: [
35 | {
36 | type: "navigation",
37 | url: "https://blog.webpagetest.org/categories/webpagetest-news/",
38 | title: "",
39 | },
40 | ],
41 | },
42 | ],
43 | };
44 |
45 | //Converting json recording to webpagetest script
46 | const script = await WPTStringifyChromeRecording(recordingContent);
47 | console.log("Stringified Webpagetest Recorder Script: \n\n" + script + "\n");
48 |
49 | // Initializing webpagetest
50 | const wptServer = "https://www.webpagetest.org";
51 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
52 |
53 | let options = {
54 | firstViewOnly: true,
55 | label: recordingContent.title,
56 | };
57 |
58 | console.log("Webpagetest Custom Script Test Result: \n");
59 |
60 | // Run the test using webpagetest script
61 | wpt.runTest(script, options, (err, result) => {
62 | if (result) {
63 | console.log(result);
64 | } else {
65 | console.log(err);
66 | }
67 | });
68 |
--------------------------------------------------------------------------------
/webvitals-crux.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://www.webpagetest.org/"; //Your URL here
7 |
8 | let options = {
9 | firstViewOnly: true,
10 | location: "Dulles:Chrome",
11 | pollResults: 5,
12 | timeout: 240,
13 | };
14 |
15 | wpt.runTest(testURL, options, (err, result) => {
16 | if (result) {
17 | console.log("<-------------Core Web Vitals------------->");
18 | console.log({
19 | CumulativeLayoutShift:
20 | result.data.average.firstView["chromeUserTiming.CumulativeLayoutShift"],
21 | LargestContentfulPaint:
22 | result.data.average.firstView["chromeUserTiming.LargestContentfulPaint"],
23 | TotalBlockingTime: result.data.average.firstView["TotalBlockingTime"],
24 | });
25 |
26 | if (result.data.median.firstView.CrUX !== undefined) {
27 | console.log("<----------------Crux Data---------------->");
28 | console.log(result.data.median.firstView.CrUX);
29 | } else {
30 | console.log("No CrUX Data Found");
31 | }
32 | } else {
33 | console.log(err);
34 | }
35 | });
36 |
--------------------------------------------------------------------------------
/webvitals.js:
--------------------------------------------------------------------------------
1 | import WebPageTest from "webpagetest";
2 |
3 | const wptServer = "https://www.webpagetest.org";
4 | const wpt = new WebPageTest(wptServer, "YOUR_API_KEY");
5 |
6 | let testURL = "https://docs.webpagetest.org/"; //Your URL here
7 |
8 | let options = {
9 | firstViewOnly: true,
10 | location: "Dulles:Chrome",
11 | pollResults: 5,
12 | timeout: 240,
13 | };
14 |
15 | wpt.runTest(testURL, options, (err, result) => {
16 | if (result) {
17 | console.log({
18 | CumulativeLayoutShift:
19 | result.data.average.firstView["chromeUserTiming.CumulativeLayoutShift"],
20 | LargestContentfulPaint:
21 | result.data.average.firstView["chromeUserTiming.LargestContentfulPaint"],
22 | TotalBlockingTime: result.data.average.firstView["TotalBlockingTime"],
23 | });
24 | } else {
25 | console.log(err);
26 | }
27 | });
28 |
--------------------------------------------------------------------------------