


WebPageTest is an online tool and an open source project designed to help developers review the performance of their websites. As a web performance evangelist for Theodo, I use it every day. I'm constantly amazed at the features it provides for the vast web development community, especially web performance workers for free.
However, things quickly get difficult when dealing with single page applications (usually written in React, Vue, Svelte, or any other front-end framework). How can you pass the login page? How do you test the performance of the user flow when most of the operations happen on the client and there is no specific URL to point to?
In this article, we'll figure out how to solve these problems (and more) and you'll be ready to test the performance of a single page application using WebPageTest!
Note: This article requires a certain understanding of some advanced features of WebPageTest.
If you are curious about web performance and want a good introduction to WebPageTest, I highly recommend the following resources:
- "How to use WebPageTest and its API" on CSS-Tricks is a great overview of the features of WebPageTest, written by Edouardo Bouças;
- "Who owns the fastest website in F1?" by Jake Archibald explains how analyzing WebPageTest results can help you improve performance in a case study on Formula One theme.
Problems with testing single page applications
Single-page applications (SPAs) fundamentally change the way websites work. Instead of letting backends (such as Django, Rails, and Laravel) do most of the heavy lifting and pass "out-of-use" HTML to the browser, SPA relies heavily on JavaScript to let the browser calculate HTML. Such front-end frameworks include React, Vue, Angular, or Svelte.
The simplicity of WebPageTest is part of its appeal to developers: Visit the https://www.php.cn/link/d346c2bc24a74cc35bc7c84444da4925 URL, wait for a while, and voila ! Your performance review is ready.
If you are building a SPA and want to measure its performance, you can rely on end-to-end testing tools such as Selenium, Cypress, or Puppeteer. However, I found that none of these tools were as much performance-related and easy-to-use tools as WebPageTest provides.
However, testing a SPA with WebPageTest can be complicated.
In many spas, most sites are protected by login forms. I often use Netlify to host my site (including my personal blog) and most of my time in the app is on authenticated pages, such as dashboards that list all my websites. Since the information on my dashboard is for me , any other user who tries to visit https://www.php.cn/link/b301bbff368b7a75043a2b9925a530ff will not see my dashboard, but will be redirected to the login or 404 page.
The same is true for WebPageTest. If I enter my dashboard URL in https://www.php.cn/link/16b2399ccd1419de9e098d7abf025eb6 , the audit will be performed for the login page.
Additionally, using simple WebPageTest audits cannot test the performance of dynamic interactions in SPAs.
Here is an example. Nuage is a domain name registrar with unique animations and beautiful, dynamic interfaces. When you search for the domain name you want to purchase, the asynchronous call will get the result of the request and display the result when the result is retrieved.
You may have noticed that in the video above, the URL of the page does not change when I type the search term. Therefore, it is impossible to test the performance of the search experience using a simple WebPageTest audit like we did for a simple search page, because we don't have the right URL to point to the action of searching content - only the URL to an empty search page.
Some other problems may arise with SPA paradigm conversion when using WebPageTest:
- Clicking around to browse the web is usually harder than just going to a new URL, but sometimes it is the only option in a SPA.
- Authentication in SPAs is usually implemented using JSON Web tokens instead of traditional cookies, which excludes the option to set authentication cookies directly in WebPageTest (as described here).
- Using React and Redux (or other application state management libraries) for your SPA may mean that forms are harder to fill out programmatically, as setting the value of a field with .innerText() or .value() may not forward it to the application store.
- Since API calls are usually asynchronous and can be used to indicate load status using various loaders, these loaders may "spoof" the WebPageTest to believe that the page is actually loaded when it is not actually loaded. I've seen this happen in longer API calls (over 5 seconds).
Since I have encountered these problems on multiple projects, I have proposed a range of tips and techniques to deal with them.
Various ways to select elements
Selecting the DOM element is a key part of performing various automation tests, whether end-to-end testing with Selenium or Cypress, or performance testing with WebPageTest. Selecting the DOM element allows us to click links and buttons, fill in forms, and interact with the application more generally.
There are several ways to select specific DOM elements using the native browser API, from the simple document.getElementsByClassName to the tricky but powerful XPath selector. In this section, we will look at three different possibilities in order of increasing complexity.
Get elements via id, className, or tagName
If the element you want to select (for example, the Clear Cart button) has a specific and unique id (for example #empty-cart), class name, or a unique button on the page, you can click it using the getElementsBy method:
<code>const emptyCartButton = document.getElementById("empty-cart"); // 或document.getElementsByClassName(".empty-cart-button")[0] // 或document.getElementsByTagName("button")[0] emptyCartButton.click();</code>
If there are multiple buttons on the page, you can filter the result list before interacting with the element:
<code>const buttons = document.getElementsByTagName("button"); const emptyCartButton = buttons.filter(button => button.innerText.includes("Empty Cart") )[0]; emptyCartButton.click();</code>
Using complex CSS selectors
Sometimes, the specific element to interact with has no interesting uniqueness attributes in its ID, class, or tag.
One way to solve this problem is to add this uniqueness manually, for testing purposes only. Adding #perf-test-empty-cart-button to a specific button is harmless to your website tagging and can greatly simplify your testing setup.
However, this solution may sometimes be unimplemented: you may not be able to access the source code of your application, or you may not be able to quickly deploy a new version. In these cases, it is useful to understand document.querySelector (and its variant document.querySelectorAll) and use complex CSS selectors.
Here are some examples that can be implemented using document.querySelector:
<code>// 选择具有`name="username"` 属性的第一个输入document.querySelector("input[name='username']"); // 选择所有数字输入document.querySelectorAll("input[type='number']"); // 选择</code>the first h1 in document.querySelector("section h1"); // choose<nav> The first direct child of<img alt="Recipes for Performance Testing Single Page Applications in WebPageTest" > document.querySelector("nav > img");</nav>
What's interesting here is that you can use the full functionality of the CSS selector. I encourage you to view the always useful MDN selector reference table!
Ultimate Solution: XPath Selector
XML Path Language (XPath) is very powerful, but it is harder to master and maintain than the CSS solution above. I rarely need to use it, but it is absolutely useful to know that it exists.
One such example is when you want to select a node based on text value and you cannot use the CSS selector. Here is a convenient code snippet that can be used in these cases:
<code>// 返回具有确切内容“2015 年9 月16 日”的<span>document.evaluate( "//span[text()='Sep 16, 2015']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;</span></code>
I won't go into detail on how to use it, as this will deviate me from the goal of this article. To be fair, I don't even know the meaning of many of the above parameters. However, if you want to read about this topic, I can definitely recommend the MDN documentation.
Commonly used case plans
In the next section, we will learn how to test performance in common cases for single page applications. I call these my test schemes .
To illustrate these scenarios, I will use the React Admin demo website as an example. React Admin is an open source project designed to build management applications and backend office systems.
It is a typical SPA example because it uses React (as the name implies), calls the remote API, has a login interface, many forms and depends on client routing. I encourage you to quickly check the website (demo account is demo/demo) to understand what we will try to achieve.
Authentication and forms
The React Admin authentication page requires the user to enter a username and password:
Intuitively, you can fill out the form and submit it by:
<code>const [usernameInput, passwordInput] = document.getElementsByTagName("input"); usernameInput.value = "demo"; // 也可以在此处使用innerText passwordInput.value = "demo"; document.getElementsByTagName("button")[0].click();</code>
If you run these commands in order in the DevTools console on the login page, you will see that all fields are reset and the login request fails when you click the button to submit. The problem is that the new value we set with .value() (or .innerText()) will not return to the Redux store and therefore will not be "processed" by the application.
Then all we need to do is explicitly tell the React value that has changed so that it will update the internal bookkeeping accordingly. This can be done using the Event interface.
<code>const updateInputValue = (input, newValue) => { let lastValue = input.value; input.value = newValue; let event = new Event("input", { bubbles: true }); let tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event); };</code>
Note: This solution is very clumsy (even according to its author himself), but it works very well for our purposes.
The updated script looks like this:
<code>const updateInputValue = (input, newValue) => { let lastValue = input.value; input.value = newValue; let event = new Event("input", { bubbles: true }); let tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event); }; const [usernameInput, passwordInput] = document.getElementsByTagName("input"); updateInputValue(usernameInput, "demo"); updateInputValue(passwordInput, "demo"); document.getElementsByTagName("button")[0].click();</code>
Long live! You can try it in the browser's console - it works perfectly.
Convert this to the actual WebPageTest script (using script keywords, single-line commands, and tab-delimited parameters) as follows:
<code>setEventName 转到登录页面navigate https://marmelab.com/react-admin-demo/ setEventName 登录exec const updateInputValue = (input, newValue) => { let lastValue = input.value; input.value = newValue; let event = new Event("input", { bubbles: true }); let tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event);}; exec const [usernameInput, passwordInput] = document.getElementsByTagName("input") exec updateInputValue(usernameInput, "demo") exec updateInputValue(passwordInput, "demo") execAndWait document.getElementsByTagName("button")[0].click()</code>
Note that clicking the Submit button will take us to the new page and trigger the API call, which means we need to use the execAndWait command.
You can view the full results of the test at this address. ( Note: WebPageTest may have archived results - however, you can still run the tests yourself again!)
Here is a short video (captured by WebPageTest) where you can see that we did pass the authentication steps:
Navigate between pages
For traditional server-side rendering pages, navigating from one URL to the next in the WebPageTest script is through navigation
However, for SPA, this does not reflect the user's experience, as client routing means that the server has no role in navigation. Therefore, direct access to the URL significantly reduces the performance of the measurement (because the time required for the JavaScript framework to compile, parse, and execute), and users do not experience this slowdown when changing pages. Since the process of impersonating users is crucial, we need to handle navigation on the client side.
Hopefully this is much easier than filling out a form. We just need to select the link (or button) that takes us to the new page and click on it! Let's continue with our previous example, although now we want to test the performance of the Comments list and the single Comments page.
Users will usually click on the Comments item in the navigation menu on the left and click on any item in the list. Checking elements in DevTools may cause us to adopt the following selection strategy:
<code>document.querySelector("a[href='#reviews']"); // 选择菜单中的“评论”链接document.querySelector("table tr"); // 选择“评论”列表中的第一项</code>
Since both clicks will result in page conversion and API calls (to get comments), we need to use the execAndWait keyword for the script:
<code>setEventName 转到登录页面navigate https://marmelab.com/react-admin-demo/ setEventName 登录exec const updateInputValue = (input, newValue) => { let lastValue = input.value; input.value = newValue; let event = new Event("input", { bubbles: true }); let tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event);}; exec const [usernameInput, passwordInput] = document.getElementsByTagName("input") exec updateInputValue(usernameInput, "demo") exec updateInputValue(passwordInput, "demo") execAndWait document.getElementsByTagName("button")[0].click() setEventName 转到评论execAndWait document.querySelector("a[href='#/reviews']").click() setEventName 打开单个评论execAndWait document.querySelector("table tbody tr").click()</code>
Here is a complete script video running on WebPageTest:
The audit results from WebPageTest show performance metrics and waterfall diagrams for each step of the script, allowing us to monitor the performance of each API call and interaction:
How is Internet Explorer 11 compatibility?
WebPageTest allows us to choose which location, browser, and network conditions to test. Internet Explorer 11 (IE11) belongs to the available browser options and will fail if you try to run previous scripts on IE11.
This is for two reasons:
- We are using ES6 syntax (such as arrow functions), which IE11 does not fully support.
- Our CustomEvent API for handling forms is not supported by IE11.
ES6 syntax problems can be overcome by converting scripts to ES5 syntax (no arrow functions, no lets and const, no array deconstruction), which might look like this:
<code>setEventName 转到登录页面navigate https://marmelab.com/react-admin-demo/ setEventName 登录exec var updateInputValue = function(input, newValue) { var lastValue = input.value; input.value = newValue; var event = new Event("input", { bubbles: true }); var tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event);}; exec var usernameInput = document.getElementsByTagName("input")[0] exec var passwordInput = document.getElementsByTagName("input")[1] exec updateInputValue(usernameInput, "demo") exec updateInputValue(passwordInput, "demo") execAndWait document.getElementsByTagName("button")[0].click() setEventName 转到评论execAndWait document.querySelector("a[href='#/reviews']").click() setEventName 打开单个评论execAndWait document.querySelector("table tbody tr").click()</code>
To bypass the lack of CustomEvent support, we can turn to polyfill and manually add one to the top of the script. This polyfill is available on MDN:
<code>(function() { if (typeof window.CustomEvent === "function") return false; function CustomEvent(event, params) { params = params || { bubbles: false, cancelable: false, detail: undefined }; var evt = document.createEvent("CustomEvent"); evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); return evt; } CustomEvent.prototype = window.Event.prototype; window.CustomEvent = CustomEvent; })();</code>
Then we can replace all the mentions of Event with CustomEvent, set the polyfill to fit a single line, and we're ready to go!
<code>setEventName 转到登录页面navigate https://marmelab.com/react-admin-demo/ exec (function(){if(typeof window.CustomEvent==="function")return false;function CustomEvent(event,params){params=params||{bubbles:false,cancelable:false,detail:undefined};var evt=document.createEvent("CustomEvent");evt.initCustomEvent(event,params.bubbles,params.cancelable,params.detail);return evt}CustomEvent.prototype=window.Event.prototype;window.CustomEvent=CustomEvent})(); setEventName 登录exec var updateInputValue = function(input, newValue) { var lastValue = input.value; input.value = newValue; var event = new CustomEvent("input", { bubbles: true }); var tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event);}; exec var usernameInput = document.getElementsByTagName("input")[0] exec var passwordInput = document.getElementsByTagName("input")[1] exec updateInputValue(usernameInput, "demo") exec updateInputValue(passwordInput, "demo") execAndWait document.getElementsByTagName("button")[0].click() setEventName 转到评论execAndWait document.querySelector("a[href='#/reviews']").click() setEventName 打开单个评论execAndWait document.querySelector("table tbody tr").click()</code>
Look!
General tips and tricks for WebPageTest scripts
Finally, I want to provide some tips and tricks to make writing WebPageTest scripts easier. If you have any suggestions, feel free to send me a private message on Twitter!
First of all, safety!
If your script contains sensitive data (such as credentials), remember to check the two privacy check boxes!
Browse the document
The WebPageTest script document is full of features not covered in this article, ranging from DNS overrides to iPhone simulations and even including if/else conditional statements.
When you plan to write a new script, I recommend first looking at the available parameters to see if there are any parameters that can help make your scripting easier or more robust.
Long loading status
Sometimes, remote API calls (for example, for getting comments) take a long time. A load indicator (such as a spinner) can be used to tell the user to wait for a while because something is happening .
WebPageTest attempts to detect when the page has finished loading by determining if there is a change on the screen. If your loading indicator lasts for a long time, WebPageTest may mistake it for an integral part of the page design and interrupt auditing before the API call returns - thus truncating your measurement results.
One way to solve this problem is to tell WebPageTest to wait for at least a certain duration before stopping the test. Here are the parameters available under the Advanced tab:
Keep scripts (and results) easy to read
- Use blank lines and comments (//) generously because single-line JavaScript commands are sometimes difficult to understand.
- Save multi-line versions somewhere as reference and line up everything when you are about to test. This helps with readability. a lot of.
- Use setEventName to name different "steps". This makes the test easier to read because it explicitly lists the reviewed page sequence and also appears in the WebPageTest results.
Iterate your script
- First, make sure your scripts are valid in the browser . To do this, remove the WebPageTest keyword (the first word of each line of the script) and copy and paste each line into the browser console to verify that everything works as expected in each step.
- Once you're ready to submit your tests to WebPageTest, start by using a very lightweight setup: run only once, a quick browser ( ahem -not IE11- ahem ), no network restrictions, no repeated viewing, an instance of the right size (Dulles, Virginia usually has good response time). This will help you detect and correct errors faster.
Automate your scripts
Your test script runs smoothly and you start getting performance reports for single page applications. As you release new features, it is important to regularly monitor their performance to detect regressions as early as possible.
To solve this problem, I am currently developing Falco, a WebPageTest test runner that will be open source. Falco is responsible for automating your audits and then presenting results in an easy-to-read interface while allowing you to read the full report if needed. You can follow me on Twitter to learn when it will be open source, and learn more about Web performance and WebPageTest!
The above is the detailed content of Recipes for Performance Testing Single Page Applications in WebPageTest. For more information, please follow other related articles on the PHP Chinese website!

For a while, iTunes was the big dog in podcasting, so if you linked "Subscribe to Podcast" to like:

We lost Opera when they went Chrome in 2013. Same deal with Edge when it also went Chrome earlier this year. Mike Taylor called these changes a "Decreasingly

From trashy clickbait sites to the most august of publications, share buttons have long been ubiquitous across the web. And yet it is arguable that these

In this week's roundup, Apple gets into web components, how Instagram is insta-loading scripts, and some food for thought for self-hosting critical resources.

When I was looking through the documentation of git commands, I noticed that many of them had an option for . I initially thought that this was just a

Sounds kind of like a hard problem doesn't it? We often don't have product shots in thousands of colors, such that we can flip out the with . Nor do we

I like when websites have a dark mode option. Dark mode makes web pages easier for me to read and helps my eyes feel more relaxed. Many websites, including

This is me looking at the HTML element for the first time. I've been aware of it for a while, but haven't taken it for a spin yet. It has some pretty cool and


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

SAP NetWeaver Server Adapter for Eclipse
Integrate Eclipse with SAP NetWeaver application server.

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

Atom editor mac version download
The most popular open source editor

Dreamweaver CS6
Visual web development tools

Dreamweaver Mac version
Visual web development tools