Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

The jsdom

Two years ago I inherited jsdom, a project of breathtaking scope. In essence, it aims to simulate a browser in JavaScript, by implementing JavaScript versions of a wide variety of web standards (much more than just the DOM). While maintaining jsdom for the last two years, and eventually bringing it to a 1.0 release, I learned an incredible amount about the web. I want to share with you what I’ve learned: the history of the standards and implementations of them that make up the web; the interaction between seemingly-disparate parts of the platform; and all about the strange APIs we’ve come to know and love. You should walk away from this talk with a new appreciation for how browsers work—and how, through the ongoing effort of a community of contributors and package maintainers, we’ve been able to recreate one in pure JavaScript.

  • Login to see the comments

The jsdom

  1. 1. THE JSDOM
  2. 2. WHAT IS JSDOM? A JavaScript implementation of the DOM*, for use with Node.js** * Actually much more than just the DOM ** Actually it runs in more places than just Node.js
  3. 3. 2 YEARS AGO…
  4. 4. jsdom in action what jsdom implements the future of jsdom
  5. 5. JSDOM IN ACTION
  6. 6. jsdom.env({ url: "http://www.dotjs.eu/", scripts: ["https://code.jquery.com/jquery-2.1.1.js"], loaded(errors, window) { console.log("Number of speakers:"); console.log(window.$(".section-speakers .thumbnail").length); } });
  7. 7. var testPage = fs.readFileSync("index.html", "utf8"); var desiredOutput = fs.readFileSync("out.txt", "utf8"); browserifyFile("entry.js").then(bundledJS => { var window = jsdom(testPage).parentWindow; var scriptEl = window.document.createElement("script"); scriptEl.textContent = bundledJS; window.document.head.appendChild(scriptEl); assert.equal(window.document.body.innerHTML, desiredOutput); });
  8. 8. http.createServer((req, res) => { var pageUrl = url.parse(req.url, true)["proxy-me"]; jsdom.env(pageUrl, (err, window) => { var images = window.document.querySelectorAll("img"); Promise.all(Array.prototype.map.call(images, imgEl => { return getImageContents(url, imgEl.src) .then(rotateImage) .then(rotatedCanvas => imgEl.src = rotatedCanvas.toDataURL()); }) .then(() => res.end(jsdom.serializeDocument(window.document))); }); }).listen(5002);
  9. 9. ZOMBIE.JS var browser = Browser.create(); browser.visit("/signup") .then(() => { browser.fill("email", "zombie@underworld.dead"); browser.fill("password", "eat-the-living"); return browser.pressButton("Sign Me Up!"); }) .then(() => { browser.assert.success(); browser.assert.text("title", "Welcome To Brains Depot"); });
  10. 10. FACEBOOK’S JEST
  11. 11. WHAT JSDOM IMPLEMENTS
  12. 12. JUST THE DOM?
  13. 13. NOPE. • DOM • HTML • DOM Parsing & Serialization • XHR • URL • CSS Selectors • CSSOM • (more crazy CSS specs…)
  14. 14. ACTUALLY, IT’S WORSE
  15. 15. (WE’RE FIXING THAT…)
  16. 16. WEB-PLATFORM-TESTS https://github.com/w3c/web-platform-tests
  17. 17. FORTUNATELY, WE HAVE HELP jsdom parse5 cssom csstyle nwmatcher xmlhttprequest canvas contextify …
  18. 18. THE FUTURE OF JSDOM
  19. 19. TMPVAR/JSDOM#950 “In the browser, you can do: window.document.querySelectorAll([ 'link[type="text/xml"]', 'link[type="application/rss+xml"]', 'link[type="application/atom+xml"]' ]); This doesn't work in jsdom.”
  20. 20. interface ParentNode { ... [NewObject] NodeList querySelectorAll(DOMString selectors); }; Document implements ParentNode; https://dom.spec.whatwg.org/#parentnode
  21. 21. String(['a', 'b', 'c']) === 'a,b,c' window.document.querySelectorAll([ 'link[type="text/xml"]', 'link[type="application/rss+xml"]', 'link[type="application/atom+xml"]' ]) === window.document.querySelectorAll( 'link[type="text/xml"],link[type="application/rss+xml"], link[type="application/atom+xml"]' )
  22. 22. WEBIDL interface Attr { readonly attribute DOMString? namespaceURI; readonly attribute DOMString? prefix; readonly attribute DOMString localName; readonly attribute DOMString name; attribute DOMString value; attribute DOMString textContent; // alias of .value readonly attribute Element? ownerElement; readonly attribute boolean specified; // useless; always returns true };
  23. 23. HTMLHRELEMENT.IDL interface HTMLHRElement : HTMLElement { [Reflect] attribute DOMString align; [Reflect] attribute DOMString color; [Reflect] attribute boolean noShade; [Reflect] attribute DOMString size; [Reflect] attribute DOMString width; };
  24. 24. ⇒ HTMLHRELEMENT.JS class HTMLHRElement extends HTMLElement { get align() { return String(this.getAttribute("align")); } set align(v) { this.setAttribute("align", String(v)); } ⋮ get noShade() { return Boolean(this.getAttribute("noshade")); } set noShade(v) { if (v) { this.setAttribute("noshade", ""); } else { this.removeAttribute("noshade"); } } ⋮
  25. 25. https://github.com/domenic/webidl-class-generator https://github.com/domenic/webidl-html-reflector https://github.com/domenic/webidl-conversions
  26. 26. https://github.com/tmpvar/jsdom

×