The power of Headless Chrome and browser automation (Google I/O '18)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC PLAYING] ERIC BIDELMAN: My name is Eric Bidelman. I'm a web developer, but basically, I'm an engineer that works in the Chrome team, but I also work in developer relations, which means I help you guys, web developers, build kind of the latest, greatest web experiences, adopt new APIs, and lately I've been focused on testing automation, headless Chrome, and Puppeteer. I think it's a really exciting space, the fact that we have headlesss Chrome now, Puppeteer. So feel free to hit me up with questions. It's @ebidel on Twitter, if you want to talk to me after the presentation. So it's really important for me to get this out of the way. This talk is not about testing. I think you should all test your apps. Don't get me wrong, it's very important. You can certainly use headless Chrome to do end-to-end testing, smoke tests, UI tests, whatever. But I want to stick to the other side of things, kind of the automation side of things. So this is something that I realized a couple weeks ago-- headless Chrome can really be a front end for your web app front end. And this was kind of an a-ha moment for me, kind of a double rainbow moment. Once I started working with headless Chrome, once I started to bake it into my, kind of, workflow as a developer, it actually makes my life easier. I can automate things, I can put headless Chrome on a server and do really interesting things with that. We'll talk a little bit how to do that. Some really cool and powerful things you can do with headless Chrome. So the agenda for today is we're going to talk about what headless Chrome is, get that nomenclature out of the way. We'll introduce Puppeteer, which is the node library that we built to work with headless Chrome, and along the way, we'll just see 10 interesting use case-driven demos that I've built that I kind of want to share with you guys. And we'll talk about Puppeteer's API to do some of those things. So that's today's agenda. This is something that I'm going to refer to a couple of times throughout today's presentation. This is the pyramid of Puppeteer. It's basically the architecture of where all of these things kind of fit together. So at the very bottom is headless Chrome. This is just the browser. And so normally, when you click on Chrome, there's this window that launches. The user can input a URL, there's a UI menu. The page is interactive, so you can type in the page, you can click around. You can even open the DevTools and tweak styles, and kind of modify the page in real time. And the DevTools, of course, has many, many more features. But with headless Chrome, there's none of that. So something is happening, right? Chrome is running, you can see it in the taskbar there, but there's literally no UI. Headless Chrome is Chrome without Chrome, so to speak. It's Chrome without UI. So if there's nothing to interact with, how is this thing useful to us? We'll talk about that. If you want to launch Chrome in Headless mode, it's just a one-line command line flag. So --headless launches Chrome without a UI. Simple. But by itself, this is not too useful. We need to combine this with something else, which is the remote debugging port flag. And you can pass any port number you want here. But once you combine these two flags, this is going to open Chrome kind of in a special mode. It's going to open this remote debugging port, and then we can tap in to the DevTools programmatically using this remote debugging port. And so that's where things get really awesome. So what does headless Chrome actually unlock for us? One of the most exciting things, I think, is the ability to kind of test these latest and greatest web platform features. Things like ES6 modules and service worker and streams-- all this goodness that's coming to the web, we can finally write apps and test those apps because we have this up-to-date rendering engine that's kind of following us as the web evolves. The other thing that it unlocks is all of this really awesome functionality that you guys are used to using in the DevTools, things like network throttling and device emulation and code coverage-- all these really, really powerful features. We can now tap into that stuff programmatically and write automation scripts and test these things and leverage some of that work that's been done for us in the past. So headless Chrome has a lot of interesting things that you can do. And I do encourage you to check out this article. This is about a year old at this point, but it's a really good article that I wrote a little bit ago, and it still is relevant. So you can do really interesting things with headless Chrome without ever having to write any code, which is kind of cool. So you can launch it from the command line, you can take screenshots from the command line, you can print to a PDF, just create a PDF of the page, and do some other interesting things so. Do check that out if you want to know just more about headless Chrome. So maybe I've sold you. Headless Chrome, it's a thing, headless browser, a thing. So what can you actually do with this stuff? Well, let's go back to the pyramid of Puppeteer. So we've got the browser at the lowest level, right? All the web platform features, all the E6 stuff, all that is at the bottom level. On top of that is the Chrome DevTools protocol. So there's a huge, kind of, layer here that the Chrome DevTools itself uses to communicate with your page and change the page. It's a whole API surface that we can tap into. These are kind of like the yin and yang for each other, so I actually rank these as among the greatest duos of all time. Headless Chrome and DevTools-- you can really take an awesome adventure with these. And, of course, you got Han and Chewie, you got PB and J, you got Sonic and Tails. But headless Chrome and DevTools-- awesome, awesome duo. So the DevTools itself, it's pretty straightforward. It is complex. There's a lot you can do with it. But it's basically just a JSON-based WebSocket API. So if you notice, I open a WebSocket to localhost 9222, which is that remote debugging port that you saw in the previous couple slides, and then you can just basically do message passing. In this example here, I'm basically getting the page's title. I'm just evaluating this document.title expression inside of the page using the runtime evaluate DevTools flag or method. And you can actually see this traffic. So DevTools itself, again, uses this protocol. So if you open the protocol monitor panel in the DevTools, you can see these requests and these responses kind of fly by. So any time you tweak a style or do something in the DevTools, you actually see the traffic for it. So you can kind of learn the API as you see this stuff happen. So back to the period of Puppeteer, we've got the browser, we've got DevTools protocol-- all this cool stuff we're going to tap into. And on top of that is where Puppeteer comes in. So Puppeteer is a library that we launched last year right around the time headless Chrome came out in Chrome 59. You can get it off of NPM. And the reason we created it was there wasn't a lot of good options for working with headless Chrome at that point in time, and we wanted to sort of highlight the DevTools protocol. Make sure people know how to use the protocol, kind of make it high-level API for some of the really powerful things you can do. So we actually use a lot of modern features. You're going to see a lot of async and await and promises in my code samples today, and that's because of this async nature of everything happening with WebSockets and Node talking to Chrome. And all that stuff is asynchronous, so promises lend themselves very nicely to that. But you can use Node 6, so if you're not in a later version of Node, you can totally use Puppeteer. Don't have to transpile or anything like that. We wanted to create a zero-configuration kind of set up for you. So when you pull down Puppeteer from NPM, we actually download Chromium with Puppeteer. That's because it's kind of hard to launch Chrome and find it, install it, on, like, a CI system. There's just a lot of issues sometimes. So we wanted to make it easy. Just bundle a version of Chrome that's guaranteed to work with the version of Puppeteer that you guys install. High-level APIs-- we'll see a bunch of examples of that and create a canonical reference for the DevTools protocol. And so that's why we created Puppeteer. So let's look at a little bit of code, a little bit of Puppeteer code. One of the most common things people do is just take a screenshot of a web page. So in order to do that, we'll call Puppeteer launch. And this is a promise. It's going to return a browser instance that we can then interact with headless Chrome. So we've got headless Chrome launched, got a browser instance, and then we'll just create a new page. And so this is going to open just a new tab in Chrome. You're not going to see it because it's headless Chrome, but it's opening about blank. And once that promise resolves, we can navigate to the URL that we want to take a screenshot of. Just call page.goto. That's going to actually wait for the page's load event to fire before it resolves. And then we can just use Puppeteer's API to take a screenshot. And this has a bunch of options. You can take a full-page screenshot or actually screenshot a portion of the page, or even a DOM element. But you can see it's pretty easy, it's very high-level. You don't need to deal with the buffers or responses or anything like that, you just pass the file you want to create and you get a .png file. And last but not least, you just close the browser out when your script is done, clean up Chrome. That'll shut down Chrome. So all in all, it's, like, four or five lines of code to do all this stuff-- launch headless Chrome, find it on various platforms, open a new page, navigate to a page, wait for its load event, take a screenshot, close the browser. So this is what I mean by the high-level APIs-- very task-driven things, but it's very easy to accomplish in Puppeteer's APIs. So pro tip-- there's headless Chrome, and this is what we use by default when you call Puppeteer launch. You're not going to see an actual browser window, but there's actually headful Chrome. So headless Chrome, headful Chrome. And this is just normal Chrome. If you include this flag, the headless false flag, this is going to actually launch Chrome. You're going to see it, and this is really handy if you're debugging scripts and you have no idea what's going on because you can't see anything. Throw this on. You can actually see Puppeteer click around, navigate pages. It's actually kind of cool to see this stuff in real time. All right, so we've got headless Chrome at the bottom with all the web platform. We've got Chrome DevTools protocol. Puppeteer is kind of a small API on top of all this stuff below us. And, of course, at the top is where our automation scripts come in. So we're already standing on the shoulders of giants because all the stuff below us has been iterated on for many years. So let's dive in. Let's see some cool stuff you can do with headless Chrome. 10 awesome things you can do with headless Chrome. The first is kind of neat. So you can actually pre-render JavaScript applications. If you don't have a good server-side render store for your framework, your app, you can actually use headless Chrome to do this for you. So I wanted to actually kind of put my words where my mouth is and build an app and see if this was actually a viable solution. So I built this devweb firehose, I call it. It's basically a content aggregator for my team. We bring in all the blogs, all the sample code. Everything we do it ends up here. And it's a real app. It's a client-side app. It's powered by ES modules and some new stuff like Firestore and Firebase Auth. It's got a JSON API so you can query the data. Its back end is written in Node and it runs Puppeteer and Headless Chrome to actually do some server-side rendering to get a good, good first meaningful paint. So this is the app. Let's see how we built it. So we can dive into the index pages, the main page of the app. And it's a basic client-side app. It's got a container that gets filled with a list of JSON posts. So I got a container here. I'm going to make a fetch request, just get the list of JSON post, and then call this magic render post method, which renders the post into this container. And all that thing does, it creates a template string, and it literally just innerHTMLs the content. And so you could use the DOM APIs or whatever. But that's basically what this app does. So the goal is to take an empty page when the page loads and turn it into a static version. That's essentially what pre-rendering a JavaScript app does. That's the end goal, and we can do this with Puppeteer. So here's a little server-side render method that does this. Basically, it'll take a URL-- the URL that you want a pre-render-- and we'll do exactly what we did before. We'll launch headless Chrome, we'll open a new page, we'll navigate to the URL that we want to pre-render. And then this new thing here, page content, is basically getting the new snapshot of the page as rendered by headless Chrome. So headless Chrome is going to process our JavaScript, it's going to render those posts, it's going to get the markup in the DOM, and we just grab that using page content. And that's the serialized kind of string of the DOM. And so that's the thing we return in this method, the updated version of the page. By itself that's useful, but we can actually put this on a web server and then server-side render this client-side app. So pre-rendering a dynamic app. Somebody will visit our app, and what headless Chrome is going to do on the server is go fetch the same page. It's going to actually go off, run the page through Chrome in the cloud. Of course it's going to see the page as written, it's going to render all that JavaScript and stuff, process my CSS. And the thing we actually return is that final result. So that's what the server returns, is that static markup. So again, empty page to static markup is the end goal. So the server itself is really easy. We're just going to reuse that server-side render method I just talked about. It's a little express server, and I'm using ES modules here. That's why we have .mjs file format and the experimental modules flag when you run this web server. So any time somebody comes to visit my home page, I basically just call that server-side render method, just load up the index.html file, the client-side app. We'll get the server side rendered, the pre-rendered version of that. It's going to go through headless Chrome being run by the browser. And then we send the HTML final response to the user, and that's the server-side rendering using headless Chrome. So you're probably wondering, is this fast? Is this actually a viable solution? So I did do a little bit of measuring of this because I was also very curious of this. So if you slow your connection down to be like a mobile device, slow the CPU down and the network, you can see the side by side comparison between the server-side rendered version on the right and the client-side version on the left. So server-side render version, immediately you see the post. As soon as the page is there, that markup is in the page, first contentful paint is right there. The client-side version takes a little longer. It's got to churn through the JavaScript, it's got to render the post, things are a little slower. So immediately you see a performance win on slow mobile devices. And the numbers actually speak for themselves. So web page test, 3G connection, just hitting this app, you go from, like, 11-second first contentful paint down to, like, a 2.3-second first contentful paint. And that's how fast those posts actually render in the DOM. So that's really exciting. Not only do we make our app faster just by adding on headless Chrome, but we also make it available to search crawlers and engines that don't understand JavaScript. So we have, kind of, two benefits. Now, in order to get to those numbers, I actually did make a few more optimizations that I want to go through because I think they highlight some of Puppeteer's really awesome APIs. So the first is you don't have to wait for the entire page to load. The only thing we care about is this list of posts, right? We only care about that markup as headless Chrome renders it. So if we go back to that server-side render method, we're launching Chrome, we're actually waiting for all network requests to be done. That's what that networkidle0 is. We don't really care about, like, our analytics library to load, we don't care about images to load, or other wasteful things. We only care about when is that markup available? So we can change the wait until here to domcontentloaded. I just want to immediately resolve this problem when my DOM has been loaded. And we can tack on one more Puppeteer API, which is page.waitForSelector. And what this is going to do is it's going to wait for this element to be in the DOM and visible. So we're waiting, we're kind of catering now this server-side render method and catering it to the app, but we're actually speeding up the pre-rendering process by doing that. So that's not waiting for the entire page to load. Number two is to cache pre-rendered results. This is kind of an obvious one, and this speeds up things quite a bit. So same method as before, but we'll just wrap it in a cache. So anytime somebody comes in for the first time, we'll fire up headless Chrome, we'll do the pre-rendering, and then store the results in the cache. And any subsequent requests just get served from that cache. It's in-memory, so you would want to do something more persistent here, but this just goes to show you that it's very easy. You only pay that penalty once for the pre-render. Number three is to prevent rehydration. So what the heck is rehydration? So if we go back to our main page, you have the container that gets kind of populated by the JSON posts. And if you think about what's happening here, the user is going to visit this page in their browser, Chrome is going to do its thing, it's going to render this in the client, but headless Chrome is also doing that on the server, so that's kind of wasteful. We're doing that twice. So the way I dealt with this was basically just look for this element that gets server-side rendered. I basically check and see if that post container gets added to the DOM, and if it's there at page load, I know that I've been server-side rendered, and I don't have to go through the hassle of fetching the posts and rendering them again. So that's another optimization you can do. Number four is to abort non-DOM requests, and here's another made-up term by myself. But what the heck are non-DOM requests? So if you think about it, we only care about this markup, and certain things don't actually build the page. So JavaScript can build a page with DOM APIs. Things like video tags and image tags and CSS, they don't actually construct markup. So we can actually abort those requests and kill those requests because they're wasteful to us. So Puppeteer and Chrome have this awesome feature called network interception. We can turn this on using SUT request interception to true. And what this is going to do is it's going to give us the ability to intercept all network requests before Chrome ever makes them. So we can listen for requests events, and inside of this, I basically just set up a whitelist. If you're one of these requests, like scripts or XHRs or fetch events that can generate markup, it will allow you to go through, it will continue the request, but if you don't, if you're a style sheet, for instance, it will just abort the request. So this is another cool way on the fly that we're speeding up the pre-rendering process. So if you want to know more about pre-rendering, headless Chrome, Puppeteer, all that good stuff that I just talked about, there is an article that I wrote a couple of weeks back. It's got more optimizations, more discussion in there. Please give me your feedback because I think this is a really cool approach, because I didn't have to change any of the code in the app, I actually just, again, tacked on headless Chrome and I got a lot of stuff for free. So I'm curious to know your guys' thoughts. Number two awesome thing you can do with Puppeteer and headless Chrome is actually verify that lazy loading is paying off. Lazy loading is a good thing. You should all do it. But sometimes, you know, I'll put in a bunch of effort into my apps, and I'll wonder, is this actually making my app faster? All this work, is it paying off? So you can verify this now using Puppeteer and the code coverage API. So we have a code coverage API that gives you kind of a breakdown of the CSS and JavaScript that your app uses. You can start cod coverage, do a bunch of stuff on the page, navigate around, and then stop code coverage. DevTools has a panel for this. You can check that out. But I wanted to go one step further and see if we can kind of analyze the page across the entire page load. So this script, basically we're going to run it on a site, and over the course of page load, on DOM content loaded, on page load, and eventually when the entire page is loaded, it's going to give us the print out of everything that's going on. So you can see the URL itself, chromestatus.com. I'm using about 37% of my JavaScript and my CSS resources at that point in time. But as the page progresses, I'm using more and more of those files, as you'd probably expect. Now, what the script actually highlights is that I'm lazy loading things. So this second resource here, these second set of bars, you can see it's not utilized at all, and that's because this resource is behind a user gesture. They have to click this navigation element, and that's the thing that actually dynamically loads this bundle. And so you can use the script like this and combine the code coverage API to determine is lazy loading paying off? Do A/B testing, do some measurements, and use Puppeteer to your advantage there. There's a cool NPM package worth checking out. If you're familiar with Istanbul, it generates these amazing HTML reports. You can basically get Puppeteer's code coverage and run this thing, and basically get the same exact Istanbul HTML output, which is really nice. So check that out. Number three is A/B testing, and there's that word "testing" again, but this is more of, like, live modifying your page without having to change the code of your page. So I want to measure if it's faster to inline styles versus just having my style sheets be link style sheets. It's a common thing people do. Is it going to pay off if I inline my styles? And normally what you would do is you would basically ship two different versions of your app and measure that. You would make code changes to measure that. But with Puppeteer, we don't have to make code changes, we can live change the page on the fly. So we'll use network interception again, but this time instead of listening for network requests, we'll listen for the responses. So for any style sheet response I get, I'm going to check the resource type. I'm just going to stash the CSS text, the content of the files inside of a map for later. We'll navigate to that URL that we want to actually measure this on, just using page.goto. And we're using now a new method, $$eval. So this is kind of like a jQuery API where you can pass it a CSS selector. In this case, I'm grabbing all the style sheets on the page. And my callback is going to get injected into the page. It's not run inside of Node, but it's actually injected into the page. So in here, you can actually run anything the browser supports-- DOM APIs, URL constructor, web platform features. And what this code does is it basically just replaces all the link tags with a style tag, and it injects the CSS content from the files inside of that style tag. So I'm just replacing the style sheets with the equivalent style tag on the fly. And that's actually what gets served up. In this case, you can run this on a server, you could do a script, do a side by side comparison. And we haven't changed the page to do this, we would just use Puppeteer to live modify the requests that are made. So that's doing A/B testing. Number four is to catch potential issues with the Google crawler. So a couple of weeks back, I built that devweb firehose app, and I realized after I pushed it to production and I hit the Render as Google button on the Webmaster Tools, that my app actually doesn't render correctly in Googlebot because it runs our super old version of Chrome, Chrome 41. Chrome 41 doesn't have CSS custom properties or all these cool, new features I was using, so I was kind of hosed. What do I do? So I said, hey, could we use Puppeteer to catch this or have an early warning signal before shipping your app? And the answer is yes, you can do this using Puppeteer. So normally, you go in the DevTools, you hit the timeline recording, you get this big, kind of, blob of information that you can slice and dice the data. We can do that, as well, programmatically using Puppeteer's tracing API. So I can start a trace and I can stop a trace whenever I want. And basically, you get this huge JSON file that you can then take action on. And one of the things that you can do is actually pull out all the features used by a page-- all the JavaScript that's used, all the APIs that get used, all the CSS that gets used-- and then you can correlate that with can I use data for Chrome 41. So that's what this script does it. You can run this script on any URL and it'll tell you the features that you're using that aren't available in Chrome 41. So Chrome status uses-- what does it use? Web components, it uses CSS contain, it uses link while pre-load. None of that stuff is available in the Google Search Bot, so this is kind of a cool early warning signal for you to determine if your app might not render correctly in Google search. And so you can then maybe load a polyfil where you didn't expect to load one before, just by getting the list of features used by the page. Number five create, custom PDFs. A lot of people like to create PDFs of their web pages. I don't really understand it, but a lot of people do. We have an API for it. So if you've joined us at the web sandbox over here this year, you can actually go up to the big Lighthouse, input a URL, and what happens is Puppeteer spawns up three different tools. It runs web page test, it runs Lighthouse, and it runs page speed insight, all at once. And then eventually what happens is you get this overall kind of report, this PDF of each one of those results from each of the tools. And we're just generating that PDF using headless Chrome and Puppeteer. So to do that, it's pretty simple. We just create a new page, and instead of navigating to a page, we're just going to construct one on the fly, just calling page.setcontent. We're kind of building an HTML page just by giving it a string. We'll set a viewport because we want the page to be big. We don't want it to be a mobile size, so we'll use the viewport and emulation APIs that DevTools has to create a big page. And then last but not least, similar to screenshots, you can call page.pdf and create a PDF of that page that you're visiting. So similar, you can create a PDF to disk. This takes a bunch of options, too. You can give it a header, a footer, style the margins. Anything that the Chrome PDF system can do you can kind of do on Puppeteer. So what's neat about this is that you don't really need a JavaScript library to create PDFs anymore, you can just use the tool that's on your system, which is the browser. That's kind of nice. Number six, make your browser talk. This is a fun one. So what this script is going to do is it's going to read a text file in Node, and it's going to open a page that uses the web speech synthesis API to read that text file back to us, so kind of a good example of combining Node and Puppeteer with some of these newer web platform features. We can take advantage of both worlds. [VIDEO PLAYBACK] - Hi there, my name is Puppeteer. I've clicked the speak button on this page to start talking to you. I'm able to speak using the browser's web speech synthesis API in the message injected into the page from Node. TLDR, the rise of the machines has begun. Bye for now. [END PLAYBACK] ERIC BIDELMAN: So we're not quite there yet, but it's kind of a cool example. So a couple of notes about this. You actually saw a window, in this case, so I'm launching headful Chrome, and that's because audio is not supported by headless Chrome. So I have to use that headless false flag in this case. The other thing that I'm doing is I'm using executable path. I'm actually opening Chrome Canary and not the bundled version of Chromium that gets installed with Puppeteer. And the reason for that is because the web speech synthesis API in Chromium, in the open source version of Chrome, doesn't have that cool British accent. It's only available inside of official builds, so I wanted the Jarvis accent. So we open this little, tiny window using those command line flags, and then we'll use a new API called evaluate on new document. What this is going to do is it's going to run this JavaScript before any of the other pages JavaScript runs. So this gets injected in the page. And what I'm doing here is I'm being silly. I'm just setting a global variable, I'm creating a global variable in the page called text to speech, and just sending it to the content of that file that I read. So that's how the message gets into the page. And then what I do is I read the page, just the HTML file, and instead of starting a web server, I just kind of navigate to the data URL version of it, so I'm just kind of on-the-fly opening the page. And then the last thing that I do is I click that speak button using page.$. Again, kind of a jQuery API where you give it a selector of an element on the page, and then we just call the click method. And that's actually what kicks off this reading of the text. Number seven awesome thing you can do with headless Chrome is to test Chrome extensions. I don't know how people tested or test their Chrome extensions, but you can certainly test your Chrome extensions using Puppeteer, which is kind of cool. So I'm going to show you guys a real example. I'm going to run the Lighthouse Chrome extension's real unit test. They decided to use Puppeteer because they would ship code every once in a while and the extension would just break, and we wanted to fix that. They wanted to actually write a test. So what this is going to do, it's going to use Puppeteer to launch a tab. I'm just going to go to Paul Irish's blog. And we're going to actually use Puppeteer to start the Chrome extension. You can see it in the corner. It's actually been started, and inside of the butter bars there at the top, you can see Lighthouse is debugging this page, and Chrome is being automated by Puppeteer. So basically what's happening is Lighthouse is just running. This is what Lighthouse does normally. It reloads the page, it gives you a report, and eventually, all the tests pass, which is really cool. So I know that was a lot. That was very fast. How did we do that? How are they actually testing their extension? So the important bit here is that we have to use headful Chrome again because headless Chrome doesn't support Chrome extensions, so we're launching a full version of Chrome. And we can use these arguments to pass in our extension directory to load Chrome with. So we'll load Chrome with our extension. And then what the Lighthouse team did was basically just grab the background page that the Chrome extension creates, and then they'll inject, they'll actually run one of the background page's JavaScript methods. So we use extensionpage.evaluate to evaluate this callback inside of the extension itself, and the extension just happens to have this method called run lighthouse in extension. That's actually what kicks off actually running Lighthouse inside the Chrome extension. So that's how they're able to test their Chrome extension. Number eight awesome thing you can do, you can crawl a single page application. Well, maybe you want to visualize your app. Maybe you don't know all the URLs of your single-page app. Maybe you want to create a sweet D3 visualization of all the pages in your single-page. Maybe you want to create a site map on the fly. You can totally do that using Puppeteer and the APIs that we have. So to do this, you can discover all the links on the page just by using page$$eval, grab all the anchors on the page. And again, this is going to get run inside of the page. And we just look for all the anchors. Are they the same origin as the page? Are they part of our app? And are they not the app we're actually viewing? So we don't want to, like, render ourselves. So we return the unique set, we'd run this recursively, and then that's basically the way that I created that D3 visualization. And you can do not just a list of links, you could do some like this with Santa Tracker. It's a very visual single-page application. As you visit each link, you can take a screenshot or generate a PDF or what have you, and then kind of visualize your app in a different way. Number nine is one of my favorites-- verify that Service Worker is actually caching all of your app. Every time I use Service Worker, I always leave something out. I always forget to catch something in the cache, which means somebody comes back to my page, and ultimately my entire app doesn't work offline. They get a 404, like an image is broken or something. Well, we can verify that that's not going to be the case using some of Puppeteer's APIs. So to do this, we'll navigate to a page that uses offline caching, and then we call page evaluate. We wait for the Service Worker in the page to be installed and ready. That's what this page evaluate method does. Next thing we do is we basically just look for all network requests. Any request that happens on the page, we'll use network interception. We'll just stash the URL to take the list of URLs that the network gets. After that, we reload the page. We want to know what's coming from the network and what gets served from the Service Worker cache, and we want to be able to determine that. So we just reload the page because at that point, Service Worker's been installed, it's cached its resources, and then we can check and see where things come from. And that's what this last line does. It basically loops through all of those requests that get made by the page, it checks their responses and determines if they've come from the Service Worker or if they've come from the network. So here's an example of the script. If you run this on the URL, you can basically see on Chrome status everything is cached, which is great. You get a little green check for all the URLs that are being returned by the Service Worker that are going to work offline, and anything that doesn't, like these analytics requests, have a little red check. So that was a choice that I made in this app to not cache analytics requests, but you can see everything else gets cached offline. So the last cool thing-- and there's many more things you can do with headless Chrome, but the last thing that I have time for is to procrastinate. So I didn't really have a good demo of the keyboard API in Puppeteer, and we have the touch APIs and stuff, you can do touch emulation. But what this is going to do is basically just open the Google Pac-Man Doodle from a couple of years ago and play it in Node. So I'm going to basically get keyboard events in Node, I'm pressing the arrow keys in Node, and eventually, the game will fire up, and then I'm forwarding those keypresses to the page. And, of course, the page just has JavaScript that knows how to handle keypresses, so I'm able to play Pac-Man in a web page in Node, and play it online. Kind of a cool example of bridging these two worlds again. Somebody likes it. So that's kind of fun. So before we wrap up, there is a number of useful things that I think I want to draw to your attention, a number of sites and tools. The first one is Puppeteer as a service-- the notion that you can run headless Chrome, kind of the browser as a web service. So a lot of the demos you saw we actually just put on handlers in the cloud. So this first one here, you pass to the URL and it takes a screenshot. It runs headless Chrome, it does all that stuff in the background. But you can kind of think about baking headless Chrome, the browser, into your web service. So that's Puppeteer as a service. We have an awesome Google Chrome Labs GitHub repository with all those demos I showed you today, as well as some other ones that we didn't talk about. A lot of useful stuff there. If you want to see anything else implemented, let me know. I'll create a demo for it. This is a cool site that we put together to just try out Puppeteer. It's called trypuppeteer.appspot.com. You can go in, you can kind of prototype ideas, play with the code, run our official demos, see the results. You don't even have to install anything to kind of work with Puppeteer. So that was a lot of stuff. We covered a lot of things headless Chrome can do, and, of course, there's a bunch of stuff that we didn't cover that it also can do. But things like server-side rendering, pre-rendering your apps, offline testing, offline verification, A/B testing, making the Google Search Bot happy, creating PDFs. So with that, hopefully you guys have realized that automation is a thing. It's not just about testing your apps, it's actually about making your app more productive and yourself as a developer more productive. So headless Chrome is a front end for your front end. With that, my name is Eric Bidelman. It's @ebidel on GitHub and Twitter, if you want to converse with me after the show. And I will leave this one up here, which has got a great list of things you can take a screenshot of. So thanks for everybody sticking around. I really appreciate you coming. [MUSIC PLAYING]
Info
Channel: Google Chrome Developers
Views: 178,377
Rating: undefined out of 5
Keywords: type: Conference Talk (Full production);, pr_pr: Google I/O, purpose: Educate
Id: lhZOFUY1weo
Channel Id: undefined
Length: 33min 46sec (2026 seconds)
Published: Wed May 09 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.