 Hello, hello, hello, hello. All right, so we got a lot to talk about. We should probably just jump in. Yeah, we do. Modern tools, testing, automation, headless Chrome, puppeteer. Sound good? There's a lot of stuff. All right, guys, we're going to cover quite a few things today. We're starting off with the DevTools. And now in the DevTools, there's a lot of things that we've been working on in the last couple months, since last Chrome Dev Summit, even since last Google I.O. So we're going to show a few of those things now. We're going to start off in authoring. We talk about authoring. I'm talking about the experience of us as developers hand crafting that user interface and delivering that awesome user experience for our users. And so there's a lot of things going on here. And the first thing I want to call out is grid layout is implemented across browsers. It's awesome. In fact, like the new Slack.com homepage, they're using grid in production. It's awesome. This monopoly experiment was on CodePen the other day. It's using grid 2. And so let's take a look and see what it looks like with DevTools. So we can open this up and inspect. And so you'll see everything laid out here with grid, the grid column grid row. If we select the parent, our little highlight overlay will be showing all the little cells of the grid. And so we can change the grid gap and see even that little highlight overlay change real time. So it's giving you a lot better understanding of how the grid is being laid out on the page. It's worth giving a shout out here to Firefox as they kind of blaze the trail in their DevTools. But we hope you're going to like this one. Now, next up, we have a new experiment about to ship for color contrast. The line in the swatch here is coming down and giving you an idea of on one side of the line, the contrast is good. On the other side, it's bad. And as you move around and select colors, it's going to give you feedback and tell you if you're hitting certain thresholds. You can select the background color and get kind of a preview right there. So please do give this a shot. Give us your feedback. Now, Eric, have you ever been in the situation where you need to change something about a site? But it's not like your site, you know, you're not running the DevServer, for instance. But you want to change something. And now you open up DevTools. Of course, you can change anything. But you know that if you have to reload the page. At least that's all the time that's gone, right? Critical changes I make in the DevTools. You wanted to change something about the JavaScript in the way that it starts. And you're like, OK, but how do I do that? It's painful. So I want to show you a brand new feature that we're calling local overrides. You can think about this as kind of offline storage for your tweaks. So first, I want to show you how we set this up. So take, for instance, the website for this event. We're going to make some changes to this. Why not? I don't have the DevServer for this, but I want to make some changes. In the Sources panel, we go to Overrides, and we're going to set this up. Now, first thing that we do, we're going to create just a folder. And this is a folder on disk that we're going to use. And it's going to store all of our changes. And DevTools needs access to it. So this point, cool. It's an empty folder. It's on disk. This folder is going to hold everything that we change. CSS files are just going to be here. And I can open these up with a text editor and save them to disk. DevTools is now going to use this. But let's make it some changes inside of DevTools and see how that works. So now, let's change JavaScript. That's kind of what I wanted to do. So we'll open up the one JavaScript file on this page. It's just some minified scripts, but we'll add some console log at the top just to make sure. Give myself a little hello. And we hit Control-S. Now, at this point, you see this little purple icon right in the top. And this is indicating that we are now overriding the sites version of this. And when I reload the page, we see the console log, which is pretty rad. Now, we'll do the exact same thing with JavaScript. So here's the production JavaScript. We'll pretty print it, because so we can see what's going on. We'll hit Control-S. And now that one is also saved to that little folder that we made. I'm going to come back to Ellen's panel and we'll make the change over there, improve the font color, I think. Maybe some pink. Oh, yeah. It's an upgrade. For sure. All right. That's good. There's one more change. Let's change the title of our talk just for fun. So changing HTML, always the tricky one, right? So here's the HTML. We'll just go down into the sources panel, find the title of the talk, and give it a little bit of an upgrade. Sounds totes profesh. All right. Now we're going to reload the page. Hope everything. Oh, yeah. Nice. So even now, everything's coming over the network, but the CSS, the JavaScript, the HTML is being updated. Even if I go to another page on the site, the JavaScript and CSS changes will still be applying to those. So I'm really excited about this. This is a feature that we've long kind of wanted the capability for. And now in DevTools, we can deliver that to you. I should also point out it's a great feature for pranks. Did you know that you were on the Verge homepage? That looks amazing. That's today? That's today? Yeah, yeah, that's today. And it's totally real because I'm reloading the page and it's definitely like, I'll do the hard reload, empty cache. It's still there. That's good. I mean, definitely high the. I think it's an improvement. Yeah, that's pretty good. All right, clutch. So many of you here need a little bit more of the DevTools. We're going a little bit farther. And so we have some new stuff here. The first thing I want to show is the performance monitor. Now performance monitor is a little different from our performance tools in the past because it shows live streaming metrics. So we can open this up in the drawer in the bottom. And as I load a page, I'm getting all sorts of new metrics. So the CPU usage broken down by category, the memory usage via the JavaScript heap, and other counters like the count of event listeners and documents and frames, and even like how many layouts and style recalculation per second that are happening. This is giving me a really good understanding of what's happening in the page as it happens. And I can correlate a lot better around my actions as I click around on the page and the kind of metrics that I see there. So we're really excited to see kind of what this unlocks for you and what you're able to learn from it. So the next thing up is the console. We spend a lot of time in the console. And on some sites, it gets really busy in there. Like this is a news site that I go to. I open up the console, there's a lot in there. And especially if I turn on verbose, like just a lot of stuff. And dealing with this kind of load in the console is tough because there might be some messages that you're looking for that you don't see. So now you can open up this sidebar panel. We're breaking down things for you by file. So these are all the messages coming from the HTML page itself. Or we can kind of view by error, by warning. This is user messages, everything used with console log and console warning, console error. And this allows you to understand which files everything is coming from and just focus on only what you care about, hopefully reducing, hopefully improving the signal to noise ratio. Now another thing that you'll notice is sometimes when there's a lot of these things in the console, many of them look the same. Things about parser blocking scripts, and you don't need to read this. But you see that there's things kind of repeating. So in Canary now, there's a new feature, which is actually on by default, called group similar. We're turning on right here. So reload the exact same page. And this time with group similar, we just say, you know what? That looks very similar to a previous thing. We're just going to kind of group it together. And now you can still open up these groups and inspect and see the individual messages. But we just wanted to clean that up for you by default so that you don't need to go hunt and reduce kind of the console spam there. So good. All right, rock and roll. So Eric, use promises? Occasionally. Occasionally? Like every day. You guys use promises? Literally every day. Yeah, yeah, yeah. Like how about in the console? This is an interesting thing. You evaluate something that returns a promise in the console. And so you'll see this thing, like promise pending. You're like, yeah, but I wanted to know what happens from the fetch. Now it is true that if you just kind of wait until you think that this promise has resolved, and then you kind of click that arrow and open it up, then it will be resolved, and you can inspect the value. That's kind of cool. But you have to guess as to when it resolves. Wait, how many people knew that? I didn't know that. Yeah, it's like an understanding. You can, but I wouldn't recommend it. I mean, slightly better is this. Like you could just do a then and send it to console log, and then it'll just print when it gets there. But typing that is kind of annoying. So we figured it would make this a bit easier for you. And now just throw the await keyword in the front of it, and then easily just get the results printed the second it happens. Can I do that in any JavaScript file? You can't just throw a wait. You do always have to be wrapped around in an ASIC function. But in the console, we just kind of took care of the complications for you and just made it easy. So you don't need to worry about that. So having to wait right here for any asynchronous stuff, just get to the value, the resolved value really easily. Now at this event, we've been talking about progressive web apps. And on DevTools, we want to make sure that you're able to not only build your first progressive web app, but scale that up to a world-class progressive web app. And so we have some new upgrades in the DevTools for this. Over in the application panel, you'll now see a graph that represents how the storage is allocated across various different types of storage. Here, this is the Twitter PWA. You can see they're using quite a bit of cache storage, a little bit of index DB, so on. Now in cache storage, there's some upgrades too. First up, on the left, where we have the listings of which caches are there. This is now live updating, which is a nice little bug fix that has really given people some headaches before. So I'm happy to say that if you add a new cache or anything, you don't need to close DevTools, reopen it. It'll just come in there. But now that you're inspecting a specific cache, there's a lot more detail in here than there was. So in addition to what files are in each cache, you'll see the size and when they were placed there, their MIME type, and their full network headers, and I really like you can also just click over and see the actual asset itself. In this case, an image. But this is really nice with JavaScript files and being able to see exactly which version of this asset is sitting in the cache at any time really helps a lot. Some similar upgrades over in the service workers panel. We've cleaned this up, gotten rid of the messy errors and old dead service workers, and there's two new features at the bottom of the screenshot here. The first helps with push notifications. So you can define a custom payload that will be coming over a push event inside the event data. So this can be text, it can be JSON, kind of be whatever you need it to be, and you just push this down, we'll emulate a push event into your service worker and you can handle it there. The second, the sync is great for working with background sync. It allows you to push down custom sync tags that your service worker can respond to. Okay, so. With progressive web apps, you've heard a bit about Lighthouse, and Lighthouse works fantastic for evaluating your progressive web app and finding the sorts of things that you still can improve on. And there's been a lot of stuff that we've been doing. And in general, I just want to back up and say Lighthouse, our goal is really to analyze your website and web app and diagnose the situation and provide concrete recommendations for how you can improve things. And our goal really with Lighthouse is just to make the web better. And we want to do that by shifting the mental load of all the details of web development, shift that away from like your brain and into a tool that can keep track of things for you. So there's been a lot happening there. We're happy that Lighthouse is available in DevTools, on the CLI as a node module. And even last year, we were on the stage, we were talking about Lighthouse. And in the last year, Lighthouse diagnosed about 50 different issues around progressive web apps and performance. And now a year later, we've doubled that. So we're tracking a lot more things inside of Lighthouse and improve the implementation of many. A few of the key highlights that I'm really keen on, some new stuff around loading performance, we're identifying what percentage of your JavaScript was executed, identifying slow server response times and identifying when your image aspect ratio has issues. Also, some nice things around SEO and your user experience. And one of my favorites now is, if your page has a JavaScript library, which has a known security vulnerability, we'll flag that for you too. Caliber is available in a lot of different tools, including some of these tools, which allow you to understand kind of your Lighthouse score and as it changes over time and kind of take care of that monitoring side of things for you. It is worth pointing out that Lighthouse works great in a continuous environment build pipeline. This is just all the packages on NPM that depend on Lighthouse. A great, like it's very enjoyable time to just browse these and see what people are building. And you'll get some inspiration for kind of the ways that you can use Lighthouse in your development workflow. There's some exciting things coming up. We've been working on a project called Lantern, which provides critical path analysis and models of what is happening inside the browser. This allows us to do a few things. One, we can run Lighthouse about five times faster than it currently takes right now. And two, it allows us to make much more accurate calculations when it comes to the performance, so we can be very precise around how much each performance opportunity is gonna save you. We're also gonna be improving the alignment of how the story is told between PageSpeed Insights, web page tests in Lighthouse, so you're not seeing conflicting results from these tools. You heard yesterday about the Chrome User Experience Report and understanding what is happening in the real world around a few performance metrics. We're gonna be incorporating this data into Lighthouse too so that from a Lighthouse report, you're seeing a snapshot of this is what just happened, but given the context of this is what's happening to all your users in the wild. And lastly, we're always gonna be continuing to surface more performance improvements that are available to you and best practices that can be improved on. I do just wanna take a moment to thank everyone who's contributed to Lighthouse. The Lighthouse community has been fantastic. We've hit over 100 contributors and really appreciate all the investment of people's time and conversations around issues and making sure that it's representing web developers as a whole, so I really appreciate that. Yes, thank you. Eric, we're gonna go back to you. It's almost Halloween, I have a joke for you. Oh boy, what's your joke? Let's hear it. Why did the headless horseman go into business? Oh, I see what you're doing here, okay. I don't know. He wanted to get a head in life. That was good, that was really good. It's not that good, but. Speaking of headless, let's talk about Headless Chrome and Puppeteer. So Paul kinda covered the manual side of things, some really awesome things you can do now in the DevTools. Let's switch gears a little bit and start automating some of that and writing programs that talk to the browser. Some really cool stuff. So what is a headless browser? Well, normally you open Chrome, right? You click a button or you launch it from the command line and you get a nice window, there's a URL bar, there's DevTools you can play with and interact with the page. But of course, with a headless browser, you get none of that. There's literally no Chrome for Chrome. You kinda take the reins, you take the controls and you tell Chrome where to go, what pages to go to, how to interact with those pages. And there's no UI to deal with, you have to write a program to control Chrome. And the way you launch Headless Chrome is really simple. You can launch it from the command line and pass it the dash dash headless flag. Pretty easy. There's a whole article that I wrote that covers Headless Chrome. There's some pretty useful things you can do with it just by itself. You don't even have to write any code. You can take screenshots and generate PDFs from the command line. You can set up a testing framework like Karma or something, all this is in this article. So if you wanna check that out, go ahead and visit us on developers.google.com. But the really interesting thing and the thing I wanna focus on today is talking to Chrome using Node.js. So we're gonna launch Headless Chrome, but we're also going to include this remote debugging port. This is really key. This is how we're gonna actually use the DevTools protocol and its APIs to control Chrome, to make it navigate to pages, to make it do things we want it to do. So that's where the magic comes in. Now if you wanna just work with Headless Chrome, there's a really awesome, very small little launcher, MPM module that we created for Lighthouse and we've taken that out and made it a separate thing because it's so useful in and of itself. Chrome Launcher basically just finds Chrome on your system. You can give it different chromes to Canary, Dev, stable channel. And just with one command, it'll launch Chrome on any platform. You can pass in that remote debugging port and pass it any flags that you wanna give Chrome. So in our case, we wanna launch Headless Chrome, we pass it dash dash headless. So super useful, easy to use Node module and then from there you can use Chrome however you want inside of your scripts. Another way you can use Headless Chrome is through one of these automation testing libraries and some of these have been around forever, Phantom.js, Industry Standard, Selenium, you can use any browser and test in your code. A lot of these for some reason are like ghosts for some. They're getting into the Halloween spirit. It's Halloween, that's true, that's very good. I don't know why, but when I kind of got into space, I was pretty daunted. I didn't know what each of these did, what browsers they support, are they using the Headless version of a browser? So this is kind of what the Chrome team saw and we said, hey, let's make at least the testing and controlling Headless Chrome really, really easy for developers, so no frills, no configuration. And so this is where Puppeteer comes in. So at high, high level, Puppeteer is just a Node.js library useful for using Headless Chrome. And by modern, what I really mean is that we're using some of these latest new JavaScript features. So if you check out Puppeteer source code, our example is you're gonna see async and wait everywhere. And there's two reasons for that. The first is promises are actually really nice. So the communicating with Chrome all happens asynchronously from Node to Chrome and back again. So promises are crucial for that. And async and wait just cleans all of that up makes things a little nicer for us. But if you want to, you can use Node 6. So really good example here is if you're like in AWS Lambda functions or you're on Google Cloud that doesn't have the latest, you know, Node 8 version installed, you can actually use Puppeteer with Node 6 without transpiling. No frills, no zero config. We actually bundle Chrome. So we'll pull down Chrome when you MPM install Puppeteer. This is really nice because you don't have to deal with those details. You can kind of just write your code and have it run and Chrome will just be there for you. It's Chrome's reference implementation of the DevTools protocol. For anybody that's looked at the protocol before, it's really big, it's very robust. You can do a lot of interesting things with it. But we wanted to kind of highlight the best practices and the really, really, really common things. So we have high level APIs for most of the common very interesting things you can do. So we've been working on this project throughout the summer, and actually today or in the very near future, we're gonna be launching a 1.0. So if you haven't checked out Puppeteer, do check it out, this is a great time to look into it. So we've got Chrome, we've got Headless Chrome, we've got Puppeteer now in the mix, DevTools protocol, all these things happening. This is the pyramid of Puppeteer. How do these things all line up with each other? So at the very bottom, we have Headless Chrome, the browser, a modern evergreen browser, latest web platform features. You can test service worker now, you can test push notifications. That's at our base. On top of that is the Chrome DevTools protocol. We're not gonna interact with that directly, but that is the thing that's gonna be talking to the browser and doing things that we want it to do. Puppeteer is this little library that wraps the Chrome DevTools protocol and sits on top of everything below us. And at the very top, that is your unit test, your smoke test, your automation scripts. The scripts are gonna write in Node.js. So I just wanna show a quick, quick comparison. Don't worry about this code too much. On the left, you're gonna see how to use the DevTools protocol. And on the right is the Puppeteer version. We're just navigating to a page and printing the HTML. Something very simple to do, but actually pretty complex to do in the DevTools protocol. And this is even a little bit shorter, I'd say, than it would be if I didn't use the Chrome Remote Interface Library. So a couple things here, I have to use a couple libraries to launch Chrome, to wrap the DevTools protocol. I have to enable some things in the DevTools protocol to say, hey, Chrome, I'm gonna use the page domain and the runtime domain. Wait for those promises, navigate to the page, wait for the load event, and evaluate some script. And on the Puppeteer side, that's just a couple lines of code, and literally you can follow it line by line just to curl the page. All right, let's see some cool examples. So first is screenshots. Really, really common case. First thing you're gonna do, obviously, is grab Puppeteer off MPM and require it in your scripts. First thing you do is you need to launch the browser, and Puppeteer has an API for that, Puppeteer.launch. This is gonna return a promise that gives you a browser instance to talk to. Now by default, it launches Headless Chrome, so you don't have to configure or use any flags here, we're just gonna get Headless Chrome. Next up is we're gonna create a new page using that browser instance. And again, everything is a promise, so you're gonna see async in a way, these are just promises, if you're not familiar with async in a way. We're gonna navigate to example.com and then call page.screenshot, that's gonna actually create a screenshot, and save it to disk as example.png. Last but not least, close the browser, we're done with it, we've ran our script, we've generated the screenshot, and in literally like five lines of code, we've opened Chrome on any platform, generated a screenshot, and boom, we're off to the races. So literally, line by line, you can tell what's going on. You're not impressed. Just screenshots, not very cool. I need more. You need more, I have more. So what else can you do? Well, there's actually really, really interesting use cases when we have headless browsing at libraries like Puppeteer. It's not just about testing, it's about automation, it's about use cases. So you can generate screenshots, you can create PDFs, that's pretty cool and useful, but maybe you wanna do something like crawl your single page application and render it server side in the cloud for SEO purposes. You can do that with Puppeteer and headless Chrome. Scrape content from sites, and we'll show an example of that, maybe you just wanna interact with a page. You can automate form submission, maybe test your accessibility or keyboard handling using Puppeteer's APIs and headless Chrome. I'm excited about end-to-end testing and having the latest JavaScript and web platform features. So we can test service worker, really, really handy if you're building a PWA. Collect page metrics. Paul showed you that in the DevTools, you can do that programmatically in Puppeteer. Hey, maybe you wanna take another tool like Lighthouse. You can use Puppeteer to change a page, modify it, test some changes, and then run it through Lighthouse to see how those changes affect performance. One of the best features is actually not a feature at all. This is it. Does anybody know what this means? Perse foot, recycle Chrome. Puppeteer's logo does look like a purse, doesn't it? That's okay. So this is my interpretation of being in lockstep with Chrome. That makes sense. With Chrome. Yeah. So what does this actually mean? It's really nice to have the DevTools team, the people that are building the browser and the DevTools, be the ones that are creating this node library. So they work really, really well together. An example, a very common example actually. Someone came to us on the Puppeteer issue tracker. They said, hey, can I emulate offline? Can I test my server server offline? We said, no. That's actually a bug in Chromium. So we go over the Chromium issue tracker, file that bug, work with the Chrome team that's working on Headless. Literally a couple of days later, the CL goes in, the commit goes into Chrome to fix this and add this new API and capability to Headless Chrome. And then in Puppeteer, we come back, notice that change, and then a couple of days later, we have a new version of Chrome that we pull in and everybody gets offline in Puppeteer. So very, very fast feedback loop now that we have all these teams kind of working together. All right, so that's enough talk. Let's see some cool examples and some of the API in Puppeteer. So similar to screenshots, hey, you can create PDFs. I don't know why people want to create PDFs. A lot of people want to create PDFs for their site. Oh, I know. So you can do that with like two, three lines of code. You know, have that, who cares? Create a PDF, emulate a screen, do whatever you want. The world is your oyster. Some more interesting things I think are emulating device. So maybe you want to test the mobile version of your site. Using Puppeteer's APIs, you know, you might not know like the viewport settings or the emulation settings for that, we have pre-canned device descriptors that have all those, the settings and widths and everything. Here, I'm just emulating an iPhone 6 and navigating to Google.com. So maybe I can screenshot that page or do what I want with it. One really powerful thing is you can run code inside, run JavaScript code inside of the page itself. So in this example, I'm going to navigate to my Twitter stream, just, you know, create a launch command, get a browser instance, create a new page, navigate to that page. What we're going to do is use page.avow to grab the first tweet on the page and then click that tweet. So this code here, this callback, this tweet.click is actually going to get run in the page. It's not in my node code. Next is we're going to wait for this, this overlay to the pop-up that Twitter shows me the full tweet. And we're just going to wait for that DOM element to become visible. And then lastly, we'll just screenshot. We'll take a screenshot of that DOM element. So that's pretty cool. You can screenshot a page, you can screenshot a full page, and you can also screenshot a DOM element. So all in all, it looks like this. We'll navigate to the page, eventually get to Twitter, we'll open the tweet, click the first tweet, and eventually we'll get a screenshot. So this is the screenshot that's produced. This is Chewy, by the way. So cute. So despite his name, he's not seven foot six and like 250 pounds, he's very small and very timid. But he's learning how to use a lightsaber, so that's important. So I don't know if you guys just saw what we did there, but we actually just wrote an integration test, a smoke test for opening a tweet on Twitter.com. You can insert your favorite framework here, but the fact that we have a real browser with a real JavaScript engine and real DOM to test against is pretty powerful. It means we can write some tests and have them work really quickly. Interception of network requests is really, really common, a really cool feature of Headless Chrome and Puppeteer. So using request, is it set request interception enable? We can actually intercept any request Chrome makes before it ever makes that request and decide what to do based on the resource type or the URL. In this example, I'm actually just aborting any request for images, saying hey, I wanna test my page, see if the layout looks good without images, see if the images fail, that's a common thing. I wanna know what it's gonna look like. So I can write a test for that using Puppeteer. Keyboard input, I can navigate to Google.com, I can pull off the DOM element that search input, type in the word Puppeteer using page.type, click Google search, wait for the search results to come. Each of those search results is an H3 with an anchor tag, I'll just wait for that selector to be visible and then just run over those links and print those to the console. So I'm just scraping content here from Google.com using Puppeteer's APIs. Paul showed you some really cool things with page metrics in the DevTools. We can get that information in Puppeteer as well, just with an API call. So same stuff is in both places if you wanna do some performance analysis. If you're getting started Puppeteer, there's a couple of really, really helpful debugging tips that I just wanna quickly touch upon, invaluable stuff. So let's talk about this launch command one more time. Now the first thing is you don't have to use Headless Chrome. You can just use Headful Chrome, full Chrome. So if you want to pop on the Headless false flag and that'll actually show you the browser window. So if you're writing a script, this is really, really useful to see what's going on. We have another flag to open the DevTools automatically. So that's kind of useful if you wanna poke around in the DevTools as your scripts are running, kind of debug things as they happen. Speaking of debugging, there's two more things, slow-mo and dump IO. Now slow-mo is something I've been showing you this entire time through all these screenshots. It actually slows down all the operations Puppeteer does. So it'll slow down typing as if it were a user. It'll slow down navigation and kind of mimic user behavior. And dump IO is really nice because it'll give you extra information from Chrome. Chrome logs a bunch of stuff and if your scripts are crashing, that's a good one to throw on. You don't have to use the Chromium that Puppeteer brings in. You can use Canary or DevChannel if you want. You can launch your own version of Chrome and just do that by passing in the executable you wanna launch. So a bunch of stuff, do check out the documentation. There's just an amazing amount of really easy to use APIs. Offline support, turn off JavaScript, setting viewport, a lot of really handy things you can do in Puppeteer. If you wanna check it out, we threw up a site called trypuppeteer.appspot.com. I hacked this together in the weekend, so if it breaks, please don't yell at me. But this is just a lot you go in, play around with some code, try out our samples. You can see any console output at the top and actually if you're generating a PDF or screenshot, I'll show you that at the bottom to get the preview. Really easy just to try, not even have to install anything. So yesterday at the keynote, Ben actually proposed a challenge, and he didn't know he was proposing a challenge to me, but I went home and I built this demo. So this is gonna be Puppeteer Inception, where you can use Puppeteer to open the trypuppeteer site, which is running Headless Chrome in the cloud that runs Puppeteer. Ooh, nice. There's like a lot of stuff going on. I love it. So let's see that. It's just using Docker and Headless Chrome. So I'm gonna navigate to the site using Puppeteer. I'm gonna inject my code and you see I'm gonna navigate to the site itself and take a screenshot of what happens. So eventually when we click the run button using Puppeteer, we'll get the screenshot within the screenshot. Pretty cool. And you can see my arrow there. It didn't move the entire time. That was Puppeteer doing its stuff. It wasn't me clicking around. So good. Actually, Eric, can I? What's this? Oh yeah, thank you, thank you. I love it. I was gonna say I wanted to interrupt you for just a second. You're talking about the DevTools protocol a second ago. And it reminds me, there's something about the protocol. If you've ever attached something else to Chrome like VS Code or WebStorm or Selenium and you've been working with it and then you open DevTools, you've seen that other tool kind of disconnect, right? Like you can only have one connected at a time. This is admittedly a bug. It's actually, it's our number one bug. And so, yeah, it's a little interesting. So I'm just gonna go and actually just take a look right now. Okay, so if, yeah, so we have 12,000 stars on this. It's a lot more than our next biggest bug. It's very disproportionate. In fact, all of Chrome bugs, it's number three. The worst part about this is that it's assigned to me. Yeah. Since 2012, what have you been doing? Not ideal. What? Yeah. We got good news. Good news is multi-client remote debugging support is now fixed. This is so good. That's cool. The great thing here is that nothing to configure on your side, everything just works automatically. Having DevTools open plus any of these things or even two of these tools together, two different things that connect to the protocol, all good, all works. It's awesome. And so now that that's done, actually we have something to take care of. Let's see. We're just going to, well, it's probably time that we close this bug. Market is fixed and hit it. Yes, go, go. Thanks Paul and DevTools team. Yeah. Yeah. That's awesome. Feels good. All right. Now on to the next one. Job well done. So with that, sorry for going over. Hope you guys learned a lot today. We did cover a lot in a short period of time. Here's all the resources, the GitHub repos for Lighthouse and Puppeteer, Headless Chrome article, DevTools protocol, if you really want to get low level, that Chrome Launcher module. I think that's it, right? Thank y'all.