WebPageTest, SPA's and Custom Scripting with Tim Kadlec

Watch the session

We walk through how to use custom scripts (and more!) to accurately test user flows and single-page applications.

Want more info? Pls follow us on Twitter: Tim Kadlec and WebPageTest

Sign up for a FREE WebPageTest account and start profiling.

Record on


Tim Kadlec​
Director of DevEx Engineering

WebPageTest, SPA's and Custom Scripting with Tim Kadlec

Event Description

We walk through how to use custom scripts (and more!) to accurately test user flows and single-page applications.

Want more info? Pls follow us on Twitter: Tim Kadlec and WebPageTest

Sign up for a FREE WebPageTest account and start profiling.


Thanks folks for tuning in, back to do another one of these things, which is always fun. Yeah, so I put out on Twitter and I put out, oh, hey Shawn. Nice to see you here, man. I haven't seen you here for a bit. Awesome. Yeah, we should catch up. So, hey everyone, thanks for coming on. Thanks for joining. I put it out there on Twitter and I also put it in the web perf slack group. But just to kind of reiterate here at any point, if anybody has recommendations on sites, they want us to look into or specific features or problem sets, problems that they're having and things like that. Let me know, we're making a list of that. So, we keep track on that and we try, even if it's not the first topic I jump on, I try to get to that at some point. So, yeah, let us know if anything like that ever pops up,

For today. Thanks. Yeah, I was going to say, this question here, who do we, you don't have to kill anyone actually, thankfully. We used to have that as a requirement. We’ve removed that. So to Jeenaj's point, if you go here, we actually have a store for it. Yeah. So keep an eye on that. Olympics yeah, Henri, you're recommending the Olympic site. I was this close to doing it this close. And they have some, if anybody's looked at this site, there are some performance hiccups there. However, what we ended up doing was getting a lot of requests, both for this. And one of the most popular things we see in the forums is, things around like scripting. Like once you get beyond like the initial load and you want to do like user flows or interactions or login, log out, authenticated stuff like, and you have to get into the custom scripting, we get a lot of asks about that.

And so I thought I would pair that with, one of the many sites that most people have been PMing about. And sort of see how that kind of show how that fits together a little bit. first though, as is part of the course, when we put out something that I'm kind of excited about, I wanted to show you, some of you probably already saw this, if you pay attention on my Twitter or follow the blog and stuff like that. But one of our, the folks here at dual [Inaudible02:24] who built our slack bot as well, also built a VS code extension. And I just want to show, cause, I think it's pretty cool. So, just show really quick, like what it does and all that jazz and I'll zoom in here, because this is going to be tiny. Give me a second.

All right. To zoom in here, can I zoom in on the actual view of the oh the user setting thing is dirty? Oh no. That's why because that's not great. Okay, cool. Let me see if I can pull that out. It's dirty. Fine. I'll just change my API key after if y'all see this. All right, here we go. So, this is the extension that was built. it's in the, marketplace now, it's kind of cool because it lets you run webpage tests from inside VS code, which I think the thing that makes it, like for me personally, what I'm excited about is, I think like the closer your testing performance to when you're writing the code the better. It's like the whole thing where you put out a bug, you catch it six months later. It's always going to be harder to solve than if you happen to catch it right as you're writing the code. Putting this in the editor is kind of fun.  

So I've got this installed. I'm going to try to pull open. Yeah. That’s what I'm going to do. I'm going to go ahead and tweak one thing in my settings docs. So y'all, don't see my API key in a separate window quick before I, uh, Show things off just because that's bad form. It's just bad security and privacy and all that jazz. Okay. So just to show you sort of the, flow for this, this is, the docs project. So our docs repo, which is based on 11ty, and Netlify, runs on Netlify. so if I'm doing local development on there, it might be kind of nice just to tweak things like if I'm experimenting with performance optimizations or improvements there, make the change and then immediately test on webpage tests just to see the impact. And the extension actually makes this possible.  

So there was a couple pieces I had to do because 11ty, uses local tunnel underneath browser sync. And it's not out of the box, turned on, but we can do that through this setting right here. So in my 11ty.JS setting or file, I have config dot set, browser sync config. You can just tell it to true or in my case I wanted a predictable domain. So I passed it and said, let's open up a WPT docs dev for the name of this tunnel. And then inside of the actual settings file I have. Oh, and look at that my API key is right back there. Oh, well I guess I'll be changing that after, you guys have about 30 minutes to abuse it.

Then for the URL to test, you can set a URL, a hard code of URL to test, which is what I ended up doing here. Now this one is a little funky local tunnel does this thing where they do, the first time you go to the local tunnel page, they put like a tunnel reminder thing, which is okay if you're doing local development and testing in your browser, you accept the reminder, move on And it's good. If you're doing testing, webpage test is going to load up a fresh view every time. And so you're going to always hit that reminder page and not actually be able to test.  

So I had to hack this a little bit. It's a little messy, but put in basically a custom script, which we'll spend more time on in today's thing, to add a header bypass tunnel reminder true. And then I'm telling it to navigate to this local tunnel address that is going to be produced, WPTdocsdev.localIt. With that in place, if I do, command bar and I type webpage test, it's going to automatically submit using that script in this case. Otherwise, like I said, you could use just a regular URL. And then the results are going to come back. Oh no did I hit the reminder? I might have, oh, you know what? Ha ha ha. I didn't actually start our local tunnel, Did I? Let’s open an integrated terminal? I really like VS code, Like I didn't expect to like code, I mean, no offense to Microsoft, but it's got a rep. Right. Like, and code was awesome. All right. So I've got my integrated terminal open, I'm going to do my 11ty stuff. Serve that up.

To answer this question, yeah, it is like Ngrok. So basically if you're running something else and you can use Ngrok to start something up, that works great too. And in that case, I don't think you have to, there's no bypass thing for Ngrok, either local tunnel, just kind of, it's one of the things they do. All right. So here in the integrated terminal, let's get rid of this piece. Can I, what is that piece? Oh, well, you can see it propped up this local tunnel address. So if I go to that that would be the site now. So now I'm going to run my webpage test command, recom, run the, thing.

Now we should have a publicly accessible URL. Which means we should actually get meaningful results. There we go. So this is, yeah. So UBC, you get the test we result, you've got the link to go back to webpage test for the full results. We've got the summary that you're used to screenshot waterfall, all right there in the editor. And this is all again, coming from that local version of this code. So now if I make a code change, I can retest and see what the impact is. So I think it's just kind of cool. It moves that sort of again, into the, editor when I'm writing the code, it's kind of neat. So if you came on that, there is a blog post, on the blog, that you can read a little bit from [Inaudible08:39] about how we built it. Yeah, just wanted to show that off. Because I thought that was pretty neat.

The plan here then is to get, we get a lot of requests, a lot of things around custom scripting. So webpagetest has the ability to do custom scripts. Now it's kind of similar to, driving it through, Cypress or, selenium or things like that, if you're used to that kind of thing. Where you are, basically programmatically telling it what to do. We can do that too with, we have our own kind of custom syntax for it, but it's not too bad once you wrap your head around some of the things. And in fact, actually I've really like parts of it because some of it is, like you're able to execute actual Java Scripts. You can go right to like the document.queryselector code to be able to get where you need to go. And things like that, which I think is really neat. And I find that very useful.

Yes. And so the questions you will run into things, with scripting, there are some intricacies, some things to pay attention to, and hopefully we'll kind of dive into that as we're doing it. Some little things that can be little gotchas or unexpected things sometimes with some of the metrics. So what I was going to look at, because a few people have independently pinged this to me. I don't know if it's, back to school thing or what, but like target has come up a few times.  

Target is an interesting example for a couple reasons. One is that target is, if anybody, if you've, they've made some noise in the past, they've made some significant pushes around performance. It's definitely an organization where they've tried to pay attention to that. It’s also interesting because of course they are large scale e-com there's personalization that happens to some extent on these pages. They’re also using react. So it's that single page application, approach, which raises its own little interesting sort of gotchas when we were talking about performance and specifically measuring performance and things like that. That’s a proper pun and I appreciate that.  

No we're actually not going to pick on them. One of the things I want to do here is also show off some of the things that I think that they're doing right as we're looking through. Because I know they have put a lot of work onto it, but I do like the pun a lot, so let's start it simple. Let’s run a test on target. We're going to keep our test runs down just to keep like to that 2 to 3 range again, just to keep things kind of moving quick. We’ll do it on our G4 which is sort of my go-to and we'll do first and repeat.

So we'll kick that off in the background now with anything like this. Like, so when we do, when you look at, if you look at like HTB archive or a lot of benchmarks. A lot of those things will always look at this homepage, but that's probably not actually the more important or even the most commonly use page on some of these, particularly when you start to consider page types and templates. So for an e-commerce site, what we're looking at is you've got your home, but there's also this whole flow where, quite a few people are going to be landing on, sort of these category pages where we're listing all the different projects. Quite a few people are going to potentially be landing directly on the product page. And then there's also the user journey part of it where we're going to start maybe at the homepage or start at a category and kind of move through and click through the site.  

There's also some value, for example, let's say we care about this. Is this, why is this under luggage? Travel cosmetic jar, I mean, I guess it's kind of Luggage, it's just really, really small luggage. Right. Okay. Okay. My phone's yelling at me. All right. So let's say we wanted to test this product display page as well. Let's fire off a test for that directly first and then I'll show you why I would want to do that differently in a second. While that's running off. Oh yeah, no, it was one of those. Yeah. One of those severe weather warning, things like that or whatever. So always fun.

All right. So here's Target's homepage as tested on a 4g network from the US Virginia on an emulated 4g. Okay. So looking at our core webvital numbers and how that lines up with the actual crux data that they're seeing, it's a pretty accurate test with one exception. It looks like our CLS is way looking significantly better than the actual 75th percentile. Like Chrome is seeing a much slower CLS score. If you've seen other streams, our guess for this is probably let's go back to that homepage and let's use Chrome's little handy dandy thing to make it look like a mobile device. Probably because of something that's happening on scroll.

Yeah. You can see, you could kind of see some of this stuff as being lazy, loaded and popping in. I'm guessing that that's going to be triggering part of our CLS issues here. The other challenge that, the single page application faces when it comes to cumulative layout shift, in particular is cumulative layout shift is based on the entire user session. And so you're, choosing the window. It’s trying to break up into the window and find like the largest like five second period or so, of shift windows and stuff like that to help with these long journey sessions. But it's going to keep looking at that as we're moving around.  

now with this being a single page application, what that means is that, if I do this and I click through to another page and then I interact with that page and keep moving around, as far as the browser is concerned, it has no idea that a next navigation has ever occurred. Like all it sees because JavaScript is hijacking this like react is taking over, doing all the routing and things like that. So the browser has no way of knowing like, hey, you've actually navigated to another page. I should stop start recording my CLS, on the, next page navigation and stuff like that and restart the whole thing.  

So the other reason why CLS might be higher in this case is because maybe there's one of those transitions or one of those interactions that's causing a big, massive shift as well. Yeah, I see, a lot of things around the stream issues. Hopefully, my connection holds up. Sorry about that. I don't have control over the weather, unfortunately. Yeah. Otherwise what I can do well, I could, that would be a bad idea probably, for multiple reasons. But what I could do is, if things get worse, I can just turn off all the video. And I'll just like be very descriptive with my voice and I'm sure everybody will still find that massively engaging and not at all weird.  

Anyway, going back to the metrics here other than that, we line up pretty well. So there's a couple things that stand out that are really good. Like first off, the start render time that we're seeing here is 1.5 seconds. That's pretty darn good for a 4g connection on a mid to low end Android device. That's not bad at all. They’ve obviously put an emphasis on that and we'll take a look at the waterfall here. I can see already from the thumbnail view that there's some, like why I think that's happening largest contentful paint a little slower. Yeah, but if we click on the waterfall, let's, let's do the whole celebrate a win thing first here.

So this light green or bright green sort of line that you see on the screen here, right after basically, when target.com the HTML response comes back. That's our first Contentful paint. And it looks like that is also our start render. Yeah. They're lining up at the same spot. Start render was yeah. For all intents and purposes, same exact metric. That is right after the HTML. They've done a really good job here of making sure that there's nothing blocking the actual initial display of the page. Like they have all these scripts. we know that's coming, it's a react app, but they're all being loaded in ways that are non-blocking which mean in this case they are deferred. Because if they would be asynchronous, Chrome would be telling us that they're potentially blocking. So these are deferred scripts, which is nice.

The CSS, they must have some CSS sitting inside the actual HTML document. They've clearly done their work and the HTML document itself 85 K it's not super light, but it's not overly obnoxious either. Potentially they could, shrink that a little bit, but they've done the work clearly to optimize that first paint, that start render experience. Now where we see the trade off right now is yes the total blocking time as Matt pointed out, that's high. That’s pretty par for the course for, a single page application. We look at this JavaScript execution. We have a massive chunk coming off of this client script. Yeah, about three seconds of CPU time there, we also have a big chunk coming off of this one, which is UGC content. That's almost two seconds there too.  

So we have these big chunks of execution, to some extent again, that's predictable.

If you're using react, you're using any sort of JavaScript SP architecture to kick off the site, it has to hydrate things up front. It has to reconstruct the Dom, get all the invent listeners, everything like that hooked up. and the challenge with that is that that's going to take time, we're working on things like I know there's work being done on progressive hydration to be able to break that hydration up and stuff like that. But right now this pattern is pretty par for the course, like big JavaScript execution up front, to get all of that said.  

now the trade off, the thing that we're hoping happens when somebody moves to an SPA or like the, the goal, or I guess the way it's been told is that you're going to pay that cost up front. But then after that subsequent routing, subsequent navigations are going to be fast because, it's taking supposedly this is the theory offsetting some of that work that the browser would be doing otherwise. So it's one of those things, you pay it up front and then hopefully it comes out in the wash as you move through the page, or through the site and navigate from view to view. And that's one of the things we can look into here in a second.

Yeah. So the question here was around first paint and what that looks like from Shawn. We can take a look at that too. Zoomed in zoomed in, so let's run down to our run3 we’ll do our film strip. Yeah. So our first paint start render time it is a bit empty and this is primarily because let's see, let's line it up here with the 2.9 seconds when we start seeing stuff is that after JavaScript execution, or simply after images, that's after images. So you see the red line here lines up with where that, film strip was. JavaScript execution for the most part kicks off after. So we're not necessarily waiting for JavaScript to kick in for displaying things. It's just that their page is extremely image heavy, like everything up front here are images in that initial view port. And so when we come down to the red line, we see that this stuff that's occurring before we actually get our stuff painted out onto the screen there, is images, images being loaded in, off the third party domain. So again like we've talked about this, us on other streams as well, but pulling stuff in from that third party domain means we're incurring a connection cost up front. We have our DNS lookup TCP connection and SSL negotiation cost in this case 340, 440, 520 mil. So half a second, basically, where we have to connect out to this domain to be able to pull those images in if we had this self-hosted or proxied somehow, through the primary domain, we'd shift this over at least 500 milliseconds.  

The other thing here is there's a lot of contention for bandwidth initially. The JavaScript is getting cued up right away here alongside that image being pulled up. And you can see there's just a little bit of silver as we're starting to request some of these other resources here, the fonts are coming in too. So we've got some bandwidth contention too competing with the images. If we really wanted to prioritize, say that LCP, image, we may want to consider, like a pre-load here for just that one. It does look like that would be a good candidate. I don't see anything else that's being pre-loaded necessarily. Maybe the font let's find out. Lot of elements this might take a bit for the search to work now, see that's coming from the inline CSS. So I don't see a preload for that, Boy searching through like 4,000 elements takes a while. Doesn't it? Apparently Chrome don't die on me now. Come on, hang in there. Big fella.

Okay. So we do have a few preloads I guess we do have the scripts being preloaded that looks like right now, the ones they must have the preload and defer sort of approach going or no. Oh, okay. All right. Hold on. He says to himself. Yeah. Okay. So remember how I said those preload scripts or those scripts were all like must be deferred because they were showing up as non-blocking. Actually what's happening here is a bug in the render blocking indicator that Chrome provides, that will be fixed in Chrome version 93. So right now these are all actually async scripts, which means they should be potentially blocking. They could potentially block because a script async can be executed before the page is displayed. So it's possibly blocking if that script arrives early before we get something painted out.  

Now they've put it towards the bottom here, which means that that risk is pretty low, but the render blocking indicator should still be giving us that. Instead it's telling us everything is non-blocking the reason for that is there's a bug right now where if a request is initiated as a pre-load request, it's immediately marked as non-blocking. And then never updated later. So that's what's happening. These are still actually potentially blocking scripts. So because they are all being pre-loaded but the bug is causing them to be labeled as just strictly non-blocking, which is why they showed up that way.  

So anyway, so scratch that pre-loading the image here would be a, eh, that's right. That's the technical way of describing it may or may not actually help us here too much. This is starting to get into the point where we're, preloading a bunch of stuff, which is always a bit of a, risky thing to do. You’re basically overruling what the browser's going to try to do from a prioritization perspective. And it's a little funky, little risky move. So this would be one of those things where yeah, maybe pre-loading the image might help, but if, so you may need to, want to, may need to want to, well, I talk good. You may want to not pre-load all those other JavaScript files as well, to get the maximum benefit out of this, otherwise you're just going to start pushing things weird and starting to mess up with, the way the browser's trying to load things in the beginning in any way.

As far as that image being the LCP image, I'm pretty positive. It was that one, but it could be one of the others. Hold on. We can find out if we go back up, click our largest contentful paint. It's this image here, which apparently Chrome neglected to be able to find the actual source for it, on this run or at least properly identify it. So let's go back to like this it's been on a stream for what, 10 minutes folks. And I've got like how many different tabs this is the way I roll. Oh, because it's pulling, it must not be pulling like the actual current source from the picture it's pulling. They have an empty alt image alt for the fallback here. Let me move the overlay. I just realize I have the overlay in the way here. So this is the actual image here that's being pulled in. However, there's like an empty image as the fallback here, it looks like in the actual source code. So it's one of these target scene, blah, blah, blah 8, 8 0 5. So if we come back over here. Yeah,

Yeah. That's the one, that is your LCP image that is 100% possible and actually probable, that it was a react pulling stuff out. All right. So anyway, so that's like just as a quick overview there, but again, what I wanted to do more here was the scripting side of things. So, so kudos to them obviously prioritize that first paint first contentful paint experience. there's still probably some stuff they could do around maybe playing with not doing all the preload on the JavaScript, getting that image, preloaded, self-hosting those images, or proxying them to get them to show up earlier, and kind of shift that LCP over. But then we also noticed that total blocking time was problematic. And the question is here, do we pay that cost again? Like what's the tradeoff here? Are we paying this three, five seconds between those two blocks alone upfront, and then maybe not ever having to do it again as we go to other pages or not.

Now, if we were to test like PDP page directly, which we did here, the product page, we're not going to get a very accurate representation of what that looks like once react is running. Instead, what we see is a fresh page load. So if I go here and I click through on the product page itself, you can see I've got some big execution again, coming in this time apparently attributed to two different scripts, which is also interesting to see that's a sign that maybe we're not actually getting any of the benefit from the SPA. We'll find out in a second. But we also got some execution happening from a client script. I think that was similar to what we had on the other page. Certainly these numbers don't look good. A 13 second LCP in this case, probably some API calls that are triggering out and pushing out our image request itself.

And again, our total blocking time is really high.  so let's see you if we have, let's see if we can get a more accurate representation of this from a user flow perspective, not saying nobody's ever going to land on that page directly, but in theory, if our theory with SPA is that we get it set up, we get react running and then by the time they navigate to that page, JavaScript is making things faster. We can test that a so this is where we're going to get back into custom scripting. So we're going to start our journey on the homepage. We’re going to keep the conditions here the same, and now we're going to start our script. So I'll pull up by the way, the docs for this, because we may have to resort to those in a minute. Well, look at that that's right. The scripting thing is all weird. Anyway, I have an extension or something interfering with scrolling in Chrome. I had a couple other people look at it. Nobody else seems to have the problem. It's just Chrome. Doesn't like me and I can't scroll down the page. So we may use Firefox for that. Anyway.  

So let's start with our script. First thing is every rescript needs a navigate command like that is, has to be there. We can use variable substitution. There’s a few different options for that. We let you substitute your URL or the host or the origin based on whatever is passed up here. So in this case, what I'm saying is navigate to the URL that I've entered in the URL bar. So that should take us to target.com. So our next step then is we're going to want to move from target to the next step in the journey. So let's say, we're going to do, let's say we click on this section here, this backpack section backpacks and lunch boxes. Everybody likes to backpacks. Oh wait, hold on. Actually, we had the, is that the same one that got us to the let's check our sale quick? Yeah, that got us to our luggage thing. So backpacks equals luggage. Oh, there's a cool baby Yoda one. I want it anyway. Yeah, detours. It's like a, like a dog with a squirrel anyway.  

So this here is what we want to click on to move to our next steps. So what I'm doing here is I'm pulling up the element panel in Chrome dev tools. You could do this anywhere. Any dev tools will work fine for this. What I want to do is I want to find the link looks like that's this. Now I could say, okay, here's our URL. Copy that link address, come back here and say, then navigate to that link address I could do that. I don't want to, the reason, I don't want to do this is because I'm going to potentially be missing out on any event handlers. Like if I really want to simulate what it's like as a user, actually clicking through this, then I need to not just navigate. I need to actually click that link.

So I'm going to come back to this. And instead, what I'm going to look for is something that I can use as a unique identifier. This feels like a good candidate. looks like there's a data attribute data, L K see luggage deals, blah, blah, blah, blah, blah. So we're going to do document that queryselector, and then we're going to drop that in. And this is, is me just kind of testing to make sure that we're right and changing my syntax a little bit and see what comes up. So if I use this command, I get this element if I hover over that element. Yeah. Okay. So that's the link we want to get. So right now I know the element we want to be able to submit the click on. So let's go back to our script and instead of navigate, we're going to replace this with an exec and wait that says, execute this JavaScript, wait until all related activity is finished. And I'm going to say, woo, not all that copied everything didn't I

Bye, bye to all that messy? And then I'm going to fire a click on that. So this should grab this element and click.  

And again, we can sort of verify that this is the, case by coming back into our console, we'll execute this command. Make sure we get to our next page. Okay, perfect. Cool. So now we're on the luggage page. So next, let's see if we can get to the, travel cosmetic jar page. So we're going to, again, basically look at this and again, we're going to look for a unique identifier here. That’s a little, it's always fun trying to parse some of the like build tools, compiled the CSS stuff to be like, I don't know. What's unique out of this? No idea. Probably not H techs bold. We could do. I wonder if the aria label one is maybe let's try that Document.queryselector what me do here. Get rid of this, make it the attribute selector. Boom. That doesn't bode well, why did it, did I, you something wrong? Oh, cuz maybe cuz it's all the weird characters in there, huh? Oh no, because I also that's. Right, right. Okay. Yeah. Do there it is. Yeah. It's all the weird characters I need to quotes around it.  

Okay. So that gives me the thanks. Sia. That's right. Yeah. So the click part, yeah, when we tell it to there's actually there's exec, there's also like a click. There is a, click and click and wait version. Now I'm not going to be able to do the cuz the darn thing does the thing we're going to go to GitHub. I got to fix whatever extension that is. That's annoying, man. We'll do source we'll do user, new. We’re getting there folks promise Dev, where is this thing? What did I do with this? You know what we can actually search. I don't know why I whispered that. It's not actually like a secret thing. It's not secret in any way, shape or form.  

Okay. I'm going to look at the actual source cuz that'll let me script cuz whatever's going on with the extension is being obnoxious. So there's things like click. I could say like click this thing. I could say click and wait. Generally I tend to use the end wait ones because it, it helps me sort of bypass like guarantee that I'm going to wait until all the related activity ends. And then there's exec and exec and wait, which is what we're doing here. I like to use exec or exec and wait rather than click and click and wait because the actual command itself is all JavaScript, which means I can validate it in the console first. Whereas like with click, like, I mean yeah, in theory, if I've got the attribute right, it's fine. But I just like the, the precision and the, like the, the feeling of comfort, knowing that I can run the exact JavaScript in the browser and verify it's going to work before I get my script going.

So we've got our exec and wait that moved us on to this page. I think we've identified using this aria label, a unique identifier there. In fact let's fire our click event on that here and make sure that works. I'll copy that. Boom. Okay. So now I can do an exec and wait there. Okay. So now, at this point we've now used the console to verify that these things should work. We should be able to navigate to target. in this case, again, the variable substitution, we should be able to, we found the link we want to click through on found a unique identifier for that, or at least unique enough. I actually think there might be more if we do queryselectorall, but queryselector grabs the first one in the, the first one's the one we wanted in this case. And then we found from there, how we move on to the next one.  

Now we still want to do a couple things here. If I used just this script, webpagetest would do all the work, and do different steps. But I want to organize it a little bit just to make it a little easier for me to see exactly what going on. So I can name these, it is event name. Suddenly doubting myself set event name. If I do set event name and I say home, and then we can come down here and be like set event, name, category, event, name, PDP, product display page for anybody who's not like the sorry, abbreviations, man. So anyway, so what this should do this, will now break each of these steps out, execute each of these things, give them some names. So that it's a lot easier for me to be able to identify, what each step is and what we're trying to measure.  

So now the moment of truth. Okay, let's do it. I'm going to actually just for purposes run this. Well, no, I can't go lower than three. I feel bad. I feel bad. Cuz one can be just like, who knows? We're going to get weird results. Maybe three is marginally better. Yeah, I can't do it. Okay. We’re going to test that in the background. No, this might take a bit, what we're doing is again, we're actually scripting those three different interactions. but what we want to see is if this gives us a more accurate version of that user flow, that user journey, then we can kind of compare to what is it like the experience for somebody who lands on a product page and they don't have react up and running, versus somebody who lands on the homepage and then makes their way to this product page. The other thing that this is going to help us with not just the impact of the SPA, but the other thing they have going on is this. if you look down here at the bottom of their waterfall, all these blue, lines, now the blue, we just added, we never had this in the legend, sorry.

The waterfall legend. We just added a few things to there to make it a little clearer, but those are requests that don't belong to the main document. So that means that the requests that are queued up by an eye frame or by a service worker, something like that. If we come down here, we see a bunch of blue, and you can see yeah. Some eye frame stuff going on, but then we also have here work box. So this is, we actually have a service worker running and getting up and running too. So that's the other thing here. We’re going to see basically the SPA and the service worker together. Once they're both up and running, what impact does that have.  

Now what we're hoping for is that by the time you go from home to category to product, anything that they're doing around caching, anything they're doing around the service worker around, react, taking over, we're hoping we're going to see dramatically, improve numbers. And certainly we're hoping that I think the primary thing we're looking for is that total blocking time. Hopefully we're not paying that price as badly on this page when we land there. If we go through the SPA with the service, worker instead. We’ll find out if that's the case or not.  

Now there will be a couple gotchas, around some of these other metrics. So things like largest Contentful paint, for example, were probably not, you can't really look at that across the steps. Largest contentful paint is going to look at the largest, piece of content that gets painted to the screen. And it's going to do that on the initial load. but then the rest of this being routed, I don't think we're going to see it unless another thing pops on the screen that is larger than our initial LCP, candidate, in which case I think the number is going to be completely skewed, because it's going to be looking from that initial load time. Chrome's going to be reporting based on that initial load, instead of like in each individual step and this is what you should expect. This is the challenge with measuring, accurately core web vitals and any other performance metrics that are based on, a page like a single actual step in a navigation journey. When you're measuring those SPAs it just gets a lot more complicated.

Shawn when you ask which page gets audited in this flow, just the last one, I assume you mean the scores, the grades. Do you mind clarifying that quick before I answer that one? Yes you do. You get all of them. So if I click on one of the grades, you can see that I've got actually a breakdown here for each of those steps. So you'll get, all of them will get something. We getting some Ns here, which maybe we'll look at what's happened on the PDP part in a second and see why. All right, so here we go. Now you can see that our metrics thing got a lot more complicated. We now have these metrics broken down by each of these steps. It's using the nice names that we put into place, to help us, which is good. Yeah, you can see, we're not getting our largest content full paint for any of these other sections because it never gets apparently larger than, than initial paint event. And then that's fine because again, if I'm measuring this from the perspective of like RUM data, real user data, I'm not going to get these either. Like this is an accurate or of what's going on there.

And yeah, that's a good point too, is in real user data that LCP algorithm is going to stop after the user clicks. All right. So let's see if we go down here the first, the home one looks good. The screenshot is, what we expected category look. Oh, oh, PDP did not bad PDP. Okay. That's all right. Let's go back and see why that was. I got a theory, let's go back to our top of the screen and well, Nope, no, no. Let's run that code, Huh? No that worked there. That's interesting. All right. Let's look back at our script Document queryselector area, blah blah, blah, blah, blah, theoretically. That should have worked. What I'm wondering is maybe the trademark is too funky. I'm not going to have detail there either. Good question. All right. Let's see if we'll just find a different attribute on that one and see if we can figure it out from there or let's grab and we can also grab a different lean related to that product. If there is one, let's try something just a little cleaner. Wonder if this class is unique. All right. So we'll grab this part of the class name.

Okay. So we're still only getting one. Let's try this and just see if it's just something, it might have been just a little too funky. Maybe webpagetest had a little bit of a hard time with that. Yeah. You can use the URL for what it's worth too. That is also a very valid point. Is Tim just like to do things the hard way. All right. Just you knows for fun. I think it's fun to know. All right. That ain't going to work. All right. But this should work. You're right. You could use the, link. But this should work as well. Yeah. That was my question. My thought at first too, which is why, I guess I kind of brushed over that and thought that through in my head, that's why I went to, see if this was lazy loaded or not. I went to this page at the start, did a fresh refresh without scrolling down and then ran that code. We can do it here with this one as well. And it seemed to of work before, again, it's working there too. So if it's lazy loading, we should be okay. But yeah.  

Yes, it will always return one element, which is what I want in this case. Did I misspeak? I might have said something else. Let's run this. Let's bring our own right. Fine Tim. We'll bring these down to the one test run and see, this is the beauty of doing it live. Isn't it? It's good. Yeah. So you could, this would be another alternative if things were being lazy loaded, in fact, to test the lazy loading, we could do things to scroll, down the page.  We certainly have the ability to inject a script to scroll down. In this case as part of our flow where we want to run it at a precise point in time, it'd be the same thing. Like we'd have, you could scroll into view again that exec or that exec and wait, takes any bit of JavaScript. So if we put together a little thing here that said, scroll until this particular element is in view or scroll down X amount of pixels or whatever, we could absolutely have a webpage test exec that JavaScript as part of the step. In fact, we could maybe break that off into, a separate event. See if there's weird things going on with scroll, et cetera.  

Let's see if this one does any better for us here should be a little faster since we're not doing all of the, Like all three runs here plus repeats. So I think we were doing like six versus this now. Should do like we need like, elevator music for things like this are running so we can just play it. I can just produce that myself going to provide the elevator music. Watch, watch the number of people on the Twitch stream. Just go. That happens. That would be, be good. While I'm doing this, I'll clean up my tabs. I don't care so much about the homepage test anymore. This is our thing. Oh.  

And here's actually opportunity to show another thing that was obnoxious for a little while. So when you're doing tests like this, you're going to like fine tune and try to play around a lot and it's going to get annoying because sometimes you get tests like this, that didn't work the way you want to of them to, like the one we just had before, they all kind of get lumped in here and it's annoying. And look at that. Somebody forgot to label their stuff properly bad, Tim. but what I can do is I can say, okay, this one here, which did not work, I do not want this, like sitting there again, confusing me. So what I'm going to do is I can actually hide test now. This is new, by popular request, do that and hide it and it's gone and now I can just pretend I never messed up. And only you, the folks in this Twitch stream will actually know.

Nice Rockey congrats. That's like, that's what you're hoping for. Right? Like you're hoping that that total blocking time, is if you're paying for it upfront, then you're not paying for it later on. Like the offset is what you're hoping for here. So that's, that's good. And it's possible. Like, that's the thing, like we don't, we do just don't know that, at scale yet we don't, cuz it's such a hard thing because there is no standard way to do that, to measure that from within the browser. So we really rely on folks who are building the SPAs to try and put those numbers out and let us know and see what the actual picture looks like there. I grabbed a different link, but we got out there. Apparently I grabbed a different link.

What did we do? Not my cosmetic jar, eh, all right. We got to a product display page. Let's look at what the total blocking time stuff happens. This is also possible since they are doing personalization and customization. It's possible we're dealing with a slightly different thing here too, since we're coming from Dallas, Virginia versus Wisconsin. Yeah. Who knows anyway, all right, let's look at what this flow looks like then together. So we do have quite a bit of blocking time still on the category page, not so much on the PDP. Now, if we went to on the repeat view, we get quite a bit, which makes sense because the repeat view is going to be not repeating all the three steps as one process, but repeating each individual step. So basically once we repeat view the PDP, we're getting that experience without the SPA, like a fresh load of that page. So that actually works as a decent comparison to some extent there.  

Okay. What we can see here is we're getting a really high total blocking cost up front. We also pay for it on the category page. It looks like the product display page is much nicer. Let’s click on an individual step and see what's going on. So we look at the category page here. So you can see we're not making an actual navigation. There's no request to dubdubdub.target/the category page, right? We're entirely within the SPA. Going out to the API to get responses, we're getting, our images that we're going to be displaying here. That's our hero image in this case. We do pay a chunk of execution up front. Now this is one of those gotchas because we broke those steps. Oh, I guess I misspoke. I was wrong about that. Repeat view does run through the entire script and sequence. Interesting.

So we have to dig and see why we've got such a big total blocking time discrepancy then between the two. one of the gotchas here is since we've got this sort of broken down by these individual events and steps, we're not usually we're able to highlight here in the waterfall, like this is where your chunk of JavaScript execution comes. That's your big pink area there. In this case, that request that it triggered all that JavaScript execution does not look like it was captured in this waterfall because it comes from this, from the home step of the journey instead. But we are seeing one big chunk, a bunch of little stuff after probably the little stuff can be attributed here for a lot of it, at least, but we are paying a big chunk on that transition.

Then when we get to the PDP transition, this is much cleaner, much nicer, by now, which that makes sense. By now, we should have, we've got two basically pages. If they're using their service workers and SPA to sort of get things up, up and running for subsequent routes, we should be pretty lightweight by now. And that's what we kind of see. There's not a ton being kicked off at this point, bunch of little chunks. And the execution seems to be extremely lightweight. So the good news is we're getting a good, transition from that category to PDP. But if I'm target I'm curious, I would be looking at this home to category transition and trying to figure out like, why are we paying such a high total blocking cost, both on that initial load and on the first route that we're taking, just not ideal.  

We can probably play with that. Where's the page. Here we go. Let's do this, let's try this. We're going to call this set event name. We're just going to call it Home to cat and we're going to use another command called combined steps. And two should be fine, combine our navigate and our exec and wait into one step. So we should just get one massive waterfall in this point, cuz otherwise again, it's going to view these as two different parts of the test and break 'em out, just like we saw. So let's run that quick and just see if we get similar things from that total blocking time transition. And if we can see some attribution there and if we actually, what I should be doing is also firing off since we know it's a JavaScript thing under the chromium tab, grabbing the V8 sampling profiler, which makes our traces obnoxiously large. But will also, give us a lot of detail in what's actually happening. And yeah, pep pointed out a good point. We can just do this and combine it all. I actually don't think I knew that. I believe I've always counted the steps my entire like 10 years of using webpagetest. Thanks pat. So that wasn't meant to be a facetious thanks by the way. I think that could have sounded like a facetious. Thanks. That's like a genuine, thank you. That's nice. We’re on that too. I should be labeling these. I'm not labeling these because I'm being lazy.

Matt, no trolling on documentation on the stream. We're trying to keep this a friendly place, man. I actually don't know if the com the combined steps might be, that behavior it's, this could be like on me for not reading the docs. Right. You're fine. Let's find out, did we do it must have closed that tab? Cause I don't see it. Combined steps. Yep. It is in the docs. Look at that, Pat nailed it right on top of it. Never doubted him defaults to zero, which is all. Okay. So your default is combined. All the things never doubted, oh, I killed it with this one though. Why did I kill it? Did I kill 'em on both?

All right. So interesting. This one just had a little bit of a funkiness. This is probably to Pat's point probably the sampling profiler, because the other version that we ran here worked fine. Although I do get like this errant, little step at the end, but I can ignore that. It doesn't appear to be doing anything there. My home to cat step is pulling in both of these and we can look at the step two thing here internally and see what's going on. But now I've got my home to category transition. The entire thing that I'm looking at. So now I can see where that execution cost is coming from when I make my next navigation, which is going to be somewhere let’s view with, our film strip view, cuz that's going to be easier.

Oh is it going to cut off on the individual step? It might. What if we bumped this to oh no, it's not. It's as being my film strip cuts off there. We might have to play with that too. All right. Let's come back to our waterfall. All right. So we've got the big thing up front from client. That's our initial load. We've got a big thing up front or coming in from UGC content. It looks like later we're are paying from vendor for a little bit. In fact there's quite a few little chunks attributed to it, to there

Right throughout. We've also got, a bunch that's being attributed to this SXX mod. I don't know what that is, but you know what SXX, that's a lot of, what is that? Four seconds of CPU time. And this would all be kind of in the later stages we're transitioning over right before we start making all those API requests. Yeah, we got a good chunk of execution kicking off there too. Yeah. The other down here. And what Matt's seeing that he is pointing out here is, all that gray is other, that's in this case going to be a lot of I'm guessing little JSON things.

Yeah. So JSON, because these are mostly going to be API targets or API requests, that are kicking off other so yeah, we've got a bunch of little individual API requests at the end that are causing all those little others to show up in our waterfall. Okay. So we are paying this where I'd be looking into here is first off we, this like that initial event or initial execution, we pay no matter what, the first time we come to a page and that SPA gets booted, that needs to be looked into there as well as what's happening in the UGC content script That's causing this as well. And then coming down, the next thing to look at is we have this SSX mod stuff that's happening as well. Just take a look at that and just see if there's anything that jumps out, move child, lots of funky stuff, lots more funky stuff. This is, yeah, this is how I talk to myself when I'm actually looking at stuff, by the way, lots of funky stuff. It just doesn't make any sense, but it's, it's not yeah. Anyway, logical in, anyway, this is what I do.  

Let's Google, what SSX mod is, just everything kind of coming up around. I'm not seeing anything necessarily that's jumping out there either. That's telling me like what it is. So maybe this is something internal to target, not sure. But there's quite a bit of execution that kicks off around the time that we navigate to another page there. So yeah, so that would be the other thing to look at for their perspective. So this is, again, this is why I find like scripting that user flow to be helpful because this paints a very different picture than like loading directly. We get to see like what is the transition cost are things that, we're attaching to these events when we're actually trying to make that transition or the clicks, that are slowing things down. how does that experience when a service worker is up and running or the SPA is up and running compared to when we're doing a direct page load, because both of those things matter. People will come to a direct and it's important to test that, but other times there are going to be people doing that whole user journey thing. And I think sometimes we don't test that enough.  

There was a great talk by the way, a few years back from Justia Perez, Cruz around performance budgets and things like that. And they talked about, I think it was domino some pizza place they did a project for and how they really focused on that journey thing. And that becomes a big thing for e-com. as you're navigating again, you've got this sort of flow from category to PDP or search to PDP and back. And then eventually even onto the cart stuff, those are things that the cart piece in particular would be an important one to be testing and stuff like that. And that's where scripting and those user flows come into play. So we can kind of see what the journey looks like.  

In fact that is something well not to sign in. I don't want to have to sign in. I was going to say, that's another thing that we could actually look at here from the scripting flow is we could pretty add to the reg no add to, is there an add to cart or is it just, is it pick it up or ship it?

Yeah. And this is a solid point. The cart is always an interesting one to test. There’s caching challenges. Often what I find is that organizations are using entirely different little mini SPAs for the cart, like are a different application for the cart than they are for the PD PPO P category kind of flow. So it's not uncommon to find that you're actually transitioning to another single page application at that point. So that part is really, really important, to test, and see what the impact is of.

I don't see. There's like a, oh, that's how I add a cart, pick it up and then view cart. Okay. So I don't know that we want to dive into it because I've already got you on here for over an hour, but that would be another thing we could test here. And we could script and again, we'd be using that same exec and wait kind of thing. So if we came down on this page and we identified what our pick it up by was this one's a little easier, Hey, look at that data test. That's nice.

Yeah. So if we fired that on a click event, then we'd have to choose our next piece would be actually view to the cart. Again, we have a nice, Easy to find the unique identifier there that should work, there. So with those two commands, like an exec and all, we could actually test that cart process as well. In fact, if you're an e-com site, this feels like a critical thing. Like even if you're not testing the flow, like test the PDP add to cart view part process because, that is where the you're making your money. And if you have performance issues there, you absolutely need to know, and this is the only, this is the way to kind of test that accurately and see what the impact is. So that would be another thing that would be worth digging into if you're any sort of e-com thing.  

And you probably have similar stuff, like if you're a travel thing, maybe it's looking at like, how do you actually book the flight? Like what's that journey look like or actually picking the flight? Those kinds of key journeys in your stuff would be important things to test this way with custom scripting. So, yeah. Thanks everyone. if there's any questions you can feel free to fire them, I can stay on for a little bit and answer a few, but otherwise thanks for tuning in. we'll be back in two weeks and again, just to sort of reiterate what I said up front, if there are things that you want us to dig into, whether there are features, a specific problem or a type of problem, or even just an individual page that could be yours could be one that you've just run into that you think, might have some interesting challenges or stuff that you'd like us to look into. Let us know. Like I said, we keep a list and we'll jump on those, but thanks everyone.

You put me on a spot. I don't like that. We’re toying. So right now we're talking to a couple people about potential guests. I know we did the one with Chris Coyer where we did the live audit. We’re looking at that for a few other folks, but we're also talking to some folks about building things. Building like some sort of extension or plugin or thing like that around the API. It just depends on timing, whether that's the next one and that's coming up, shortly after, but that's on the docket for sure. I've also had a few folks kind of request things around like images specifically, diving and things like that. So there's quite a few things we could do on that line. We could also to, Sean's point here could also play around with more stuff where we dig deep in the JavaScript part of things, that sampling profiler part does capture a detailed flame chart of what's going on inside a Chrome.

So we could also look at that, from, from that angle too, and actually dive into the flame chart and see what's happening from a JavaScript execution. So that's on the docket too at some point. and then we have, I think just probably at this point half a dozen different URLs that put bill past, which are always fun to kind of dive into though. Cause you always, there's always something different. Like my experience is, there's some core bottlenecks that tend to be everywhere. But there's always like one or two things, at least on every site that's unique and very interesting. It gives us a chance to kind of dive into something different. So I can add that to the list too. That would be a good idea.

And yes, on Henri to answer that is in two weeks. So we do every other week, same time, same channel. I get to say that cuz it's literally a channel. And the other thing too is we're starting, its fledgling is the word I believe, right? Like, oh boy, I'm trying to use words that I'm not a hundred percent sure are the right word fledgling. Let's look it up a young bird that is just fledged. That's not helpful, immature inexperienced or underdeveloped. All right. Kind of anyway, that's about right. We have a YouTube channel where are starting to put together. We're also posting all these Twitch videos there. So, that will be a place to, keep, find these two after the fact, if you ever want to go back to these, you can watch them on Twitch. But you can also check out the YouTube channel and we'll have them posted there too. All right. Thanks gang.

Site Speed
Core Web Vitals
Website Performance

Sign up for a FREE WebPageTest account and start profiling


Tim Kadlec​
Director of DevEx Engineering