<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hendrik</title>
    <description>The latest articles on DEV Community by Hendrik (@hoverbaum).</description>
    <link>https://dev.to/hoverbaum</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F83642%2F99bcd8ec-b7de-412e-b35f-e934f62e6607.jpeg</url>
      <title>DEV Community: Hendrik</title>
      <link>https://dev.to/hoverbaum</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hoverbaum"/>
    <language>en</language>
    <item>
      <title>Today I learned about "place-items"</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Wed, 05 Oct 2022 15:26:11 +0000</pubDate>
      <link>https://dev.to/hoverbaum/today-i-learned-about-place-items-37a9</link>
      <guid>https://dev.to/hoverbaum/today-i-learned-about-place-items-37a9</guid>
      <description>&lt;p&gt;Today I learned about the CSS property &lt;code&gt;place-items&lt;/code&gt;, it allows you to easily position elements in flex and grid layouts. It also solves the eon old quest to "center something in a div".&lt;/p&gt;

&lt;p&gt;To center the single child of a div simply add &lt;code&gt;display: grid;&lt;/code&gt; and &lt;code&gt;place-items: center;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6pllkbehjc83atzqki1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6pllkbehjc83atzqki1.png" alt="Center elements in a div using display:grid and place-items:center."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/place-items" rel="noopener noreferrer"&gt;Place-items&lt;/a&gt; takes up to two parameters. The first to place the items vertically and the second to place them horizontally. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcqz59lhrunph2o353ffc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcqz59lhrunph2o353ffc.png" alt="Examples of place-items."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This concludes out TIL, see you next time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.wrapper-that-centers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;place-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxg2qg6dst36zrb03zj9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxg2qg6dst36zrb03zj9.png" alt="Teacher Emoji for TIL."&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>todayilearned</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Trying my hands at Deno 1.0</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Wed, 27 May 2020 11:13:19 +0000</pubDate>
      <link>https://dev.to/hoverbaum/trying-my-hands-at-deno-1-0-2ci8</link>
      <guid>https://dev.to/hoverbaum/trying-my-hands-at-deno-1-0-2ci8</guid>
      <description>&lt;p&gt;Following the release of &lt;a href="https://deno.land/v1"&gt;Deno v1.0&lt;/a&gt; I got excited to try my hands at it. These are my first experiences writing a simple tool in Deno.&lt;/p&gt;

&lt;p&gt;A super fast introduction to &lt;a href="https://deno.land/"&gt;Deno&lt;/a&gt;:&lt;br&gt;
Deno is the spiritual successor of Node trying to fix &lt;a href="https://www.youtube.com/watch?v=M3BM9TB-8yA"&gt;design mistakes&lt;/a&gt; that were made early on but recognized only late into the project. Deno supports TypeScript out of the box and relies on web-standards. In Deno you can import ES modules from any URL and use fetch like you would in the browser. To help unify the community on processes and workflows Deno provides a wide array of &lt;a href="https://deno.land/std"&gt;stdLibs&lt;/a&gt; and has build in solutions for bundling, testing and code formatting. You can read more in the &lt;a href="https://deno.land/v1"&gt;Deno v1 release post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To try my hands at Deno and collect some experience on how ready it is I decided to implement a simple cli. The goal: read all markdown files in the current directory, parse them to HTML, add a navigation and output them into a new folder. I also decided to use &lt;a href="https://newcss.net/"&gt;new.css&lt;/a&gt; as a class-less CSS library to style the pages.&lt;/p&gt;

&lt;p&gt;You can go and checkout the &lt;a href="https://github.com/HoverBaum/md_website/tree/v1.0.0"&gt;finished repo&lt;/a&gt; or an &lt;a href="https://md-website-example-nugz74lgw.now.sh/"&gt;example website created&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Organizing
&lt;/h2&gt;

&lt;p&gt;First things first. A plan on how to implement the described tool. We will tackle it through the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Walk through all files in the directory, getting a list of markdown files.&lt;/li&gt;
&lt;li&gt;Iterate over all those files and parse them into HTML.&lt;/li&gt;
&lt;li&gt;Add a navigation and header to each page.&lt;/li&gt;
&lt;li&gt;Output the files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While following these steps we will see Denos features in action. Let's get started.&lt;/p&gt;

&lt;p&gt;If you are a VSCode user I highly recommend the &lt;a href="https://marketplace.visualstudio.com/items?itemName=justjavac.vscode-deno"&gt;Deno extension for VSCode&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting the files
&lt;/h2&gt;

&lt;p&gt;First we need to walk through the files and find all the markdown ones. Lucky for us Denos fs stdLib includes a &lt;a href="https://deno.land/std/fs#walk"&gt;walk&lt;/a&gt; function that does just that.&lt;/p&gt;

&lt;p&gt;The docs also tell us how to use this correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;printFilesNames&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But we only want markdown files and maybe set some more options for this.&lt;/p&gt;

&lt;p&gt;After some digging I found the available options in &lt;a href="https://deno.land/std/fs/walk.ts"&gt;walks source&lt;/a&gt;. Which makes this entire thing feel quite immature. But equipped with that knowledge I arrived at a function to get all markdown files from a directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;findMarkdownFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;walkOptions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;small&gt;Feel free to correct my coding here (still new to generator functions).&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;On the way though I noticed that the docs were incorrectly not providing a path (e.g: &lt;code&gt;"."&lt;/code&gt;) as the first argument to &lt;code&gt;walk&lt;/code&gt;. Quickly opened a &lt;a href="https://github.com/denoland/deno/pull/5668"&gt;PR to update the docs&lt;/a&gt; so the next person won't face the same half hour of struggling.&lt;/p&gt;

&lt;p&gt;Outputting the result we have our list of files that we want to work with, Step 1 ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Parse the markdown files to HTML
&lt;/h2&gt;

&lt;p&gt;There are a TON of libraries on NPM that you can use to parse Markdown into HTML. Sadly they do not export ES Modules...&lt;/p&gt;

&lt;p&gt;After spending some time digging through &lt;a href="https://www.pika.dev/"&gt;pika.dev&lt;/a&gt; which feel like as close as you can currently get to a central place of searching for ES Module dependencies, I was stuck. For some time I tried different libraries, importing them in different ways but usually they were missing some dependency. There is currently an initiative to support Node modules in Deno but it's still underway. And most NPM packages simply are not ES Modules. &lt;/p&gt;

&lt;p&gt;In the end I found &lt;a href="https://github.com/developit/snarkdown"&gt;snarkdown&lt;/a&gt; which didn't have any active development for a year and only supports very basic markdown parsing. It will be good enough for this example project but no where near anything I would want for a real project.&lt;/p&gt;

&lt;p&gt;Bringing it in via pika was a breeze and I also enjoyed playing around with the &lt;a href="https://www.pika.dev/npm/snarkdown/repl"&gt;REPL feature on pika&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;snarkdown&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.pika.dev/snarkdown/^1.2.2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseMarkdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mdFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MDFile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;HTMLFile&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;snarkdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mdFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;originalPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mdFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;originalName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mdFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mdFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/md$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mdFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;md$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's not sophisticated, it took waaaaay longer than I would want to find a module to use, but parsing is working. Step 2 ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a navigation
&lt;/h2&gt;

&lt;p&gt;This is the part where native TS support becomes awesome. You might have seen in the code above that I created some types for the objects I am handing around. Thanks to that it is just super easy to know what exactly I have available later down the pipe.&lt;/p&gt;

&lt;p&gt;On a side note a painful lesson I learned in an earlier project is that collecting all items is a necessity for supporting features like a navigation in a markdown to static website tool.&lt;/p&gt;

&lt;p&gt;Equipped with that and some String Literals we can easily wrap the created HTML inside a whole HTML page with a nice title, navigation and inclusion of the new.css styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addSurroundings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headerCreator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLFile&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ParsedFile&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/title&amp;gt;
    &amp;lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css"&amp;gt;
    &amp;lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css"&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;headerCreator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)}${&lt;/span&gt;&lt;span class="nx"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
&amp;lt;/body&amp;gt;`&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And we have files with a navigation and styling, ready to be saved. A simple step, without dependencies. Greatly aided through TypeScript, Step 3 ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving new files
&lt;/h2&gt;

&lt;p&gt;Here I started to get into the thick of things where in the end I searched for help on &lt;a href="https://discord.gg/deno"&gt;Denos Discord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In theory Deno also has a stdLib to save files. It's also part of fs and called &lt;a href="https://deno.land/std/fs#writefilestr"&gt;writeFileStr&lt;/a&gt;. The docs are also straight forward, just import it all from the mod.ts file. That is the current convention to export a module for Deno; create a mod.ts that exports everything.&lt;/p&gt;

&lt;p&gt;Unfortunately just using an import as the docs say gave me an error at runtime. Reading through the &lt;a href="https://deno.land/std/fs#usage"&gt;docs&lt;/a&gt; I realized that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All the following modules are exposed in mod.ts This feature is currently unstable. To enable it use deno run --unstable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay so I have to use &lt;code&gt;--unstable&lt;/code&gt;, that's gonna make my little tool look trustworthy to users. But, let's give it a go.&lt;/p&gt;

&lt;p&gt;After trying that I ran into the next error....&lt;/p&gt;

&lt;p&gt;This time, even though I am importing all the modules from master there seems to currently be an incompatibility. Luckily the stdLibs come versioned and the version can be added to the URL to fix all dependencies to the same. There is an &lt;a href="https://github.com/denoland/deno/issues/5175"&gt;issue about this&lt;/a&gt;, if you want to dive deeper into the topic.&lt;/p&gt;

&lt;p&gt;Long story short: after some friendly discussions on and great help from Denos Discord server I opted to fix the versions of my dependencies and import them from the sub modules to not force my users to use &lt;code&gt;--unstable&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We use:&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ensureDir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://deno.land/std@0.51.0/fs/ensure_dir.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Instead of:&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ensureDir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://deno.land/std/fs/mod.t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It was an adventure that taught me a lot about Denos state and internals but finally, Step 4 ✅&lt;/p&gt;

&lt;p&gt;And we can move onwards to using the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it
&lt;/h2&gt;

&lt;p&gt;With Deno installed you can try the tool right now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run &lt;span class="nt"&gt;--allow-read&lt;/span&gt; &lt;span class="nt"&gt;--allow-write&lt;/span&gt; https://raw.githubusercontent.com/HoverBaum/md_website/v1.0.0/index.ts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Deno can simply run scripts from URLs which feels refreshingly easy to get started with. The above uses a version to make sure it keeps being the tool this post talks about.&lt;/p&gt;

&lt;p&gt;You can also install the script and make it generally available on your machine using the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;deno &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--allow-read&lt;/span&gt; &lt;span class="nt"&gt;--allow-write&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mdw https://raw.githubusercontent.com/HoverBaum/md_website/v1.0.0/index.ts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will install version 1.0.0. It also sets permissions that the tool needs to run. We need read and write access to read the Markdown files and write the parsed HTML. &lt;code&gt;--force&lt;/code&gt; makes sure to replace previous installations and &lt;code&gt;--name mdw&lt;/code&gt; aliases the cli to &lt;code&gt;mdw&lt;/code&gt; in your terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;On this journey I go to explore many aspects of Deno, learn a lot and build a fun little tool. All in all I enjoyed working with Deno. But let's take a closer look at where things are at.&lt;/p&gt;

&lt;h4&gt;
  
  
  Standardisation and Modules
&lt;/h4&gt;

&lt;p&gt;Deno is just starting out with v1.0 being about a week old at time of writing this. And Deno opted to not use a central repository but instead distributed hosting. We will need to see whether federations of Module repositories arise that can leverage the potential of this distributed architecture or if Pika (or similar) will rise to be the NPM for Deno.&lt;/p&gt;

&lt;p&gt;The bigger issue I found in my small project was the unavailability of ES Modules to support my usecase. That might have been something specific for me though. Generally I am a huge fan of going with Webstandards as they are here to stay and I think this issue will continue to grow smaller, as everyone races to build the "awesome X for Deno" Module.&lt;/p&gt;

&lt;h4&gt;
  
  
  Community
&lt;/h4&gt;

&lt;p&gt;Denos community is currently hyper-active, as everyone is racing to build the cool things and new standards for X in Deno. I experienced the GitHub issues to be active and the Discord server to be a welcoming place.&lt;/p&gt;

&lt;p&gt;There are a couple of discussion points that have potential to split the community, like distributed dependencies but all in all I feel Denos community is a great and growing one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Last words
&lt;/h4&gt;

&lt;p&gt;Developing my first little tool in Deno was fun. The entire ecosystem does not yet feel production ready, even though it reached v1.0. But Deno is surely a technology you want to keep on your radar and evaluate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nWmvUQSd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h2ox4jkbtgq7omdhhdhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nWmvUQSd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h2ox4jkbtgq7omdhhdhq.png" alt="Sauropod Emoji"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>deno</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Log to Elasticsearch using curl</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Tue, 14 May 2019 11:27:10 +0000</pubDate>
      <link>https://dev.to/hoverbaum/log-to-elasticsearch-using-curl-4dpi</link>
      <guid>https://dev.to/hoverbaum/log-to-elasticsearch-using-curl-4dpi</guid>
      <description>&lt;p&gt;Once you get a centralised logging solution like Elasticsearch setup you open up an incredible amount of possibilities. That is if you actually send logs to your central place. On a recent project, we wanted to &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"just log our deploys from the Jenkins Server". &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thus we set out to find the "easiest" way to log to our Elasticsearch. We finally reached curl as our solution and today I will walk you through how to do that.&lt;/p&gt;

&lt;p&gt;This post uses Elasticsearch version 7. We also focus on Mac and Linux terminals for commands. Some of them might work differently on Windows, I suggest using &lt;a href="https://gitforwindows.org/" rel="noopener noreferrer"&gt;Git Bash&lt;/a&gt; to get around those issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Console basics
&lt;/h2&gt;

&lt;p&gt;If like me, you find the command line highly fascinating for it's myriad of tools and how simple tools can be composed into bigger workflows, much like good software is, BUT you have no idea what you are actually doing (also like me), follow along as we dive into some tools we will use today. Should you be a firm terminal hero, feel free to jump this section.&lt;/p&gt;

&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://curl.haxx.se/" rel="noopener noreferrer"&gt;curl&lt;/a&gt; is a free and open source command line tool to transfer data over the internet. As such you can use it to make GET requests or send POST requests to servers. Let's look at a simple example you can try out:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl https://jsonplaceholder.typicode.com/posts/1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will fire a simple get request towards the &lt;a href="https://jsonplaceholder.typicode.com/" rel="noopener noreferrer"&gt;JSON placeholder API&lt;/a&gt; and return the post with index 1. curl will log the result to the console for you. Through this logging, to the console, you can also chain curl with other commands, like grep the title of the post. But a deeper dive into chaining is a topic for another day.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl https://jsonplaceholder.typicode.com/posts/1 | &lt;span class="nb"&gt;grep &lt;/span&gt;title


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let us instead focus on understanding curl and relevant options better. First, we will need to specify the method (POST) we want to use. For that we can use the &lt;code&gt;-X&lt;/code&gt; (pay attention to the capital X) option and pass in the method we would like to use. Our placeholder API from before also allows us to post to it, so let us try:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://jsonplaceholder.typicode.com/posts


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we will want to send some data along. For this curl has &lt;code&gt;-d&lt;/code&gt; to pass data along as well as &lt;code&gt;-H&lt;/code&gt; to set a header. We want to set a header for the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" rel="noopener noreferrer"&gt;Content-Type&lt;/a&gt; so that we can tell receivers that we are sending JSON. Try the code below and also try it without the &lt;code&gt;-H&lt;/code&gt; option to get a feeling for what we get by using it. You will notice that the placeholder API interprets out entire data as a key with no value specified. Setting the right Content-Type helps us to pass the data along in a way that the server can handle. You might run into an &lt;a href="https://stackoverflow.com/a/22883631/2156675" rel="noopener noreferrer"&gt;issue with ' on Windows&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://jsonplaceholder.typicode.com/posts &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"title": "My awesome title"}'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Read more about the used options in the curl manual &lt;a href="https://curl.haxx.se/docs/manpage.html#-X" rel="noopener noreferrer"&gt;-X&lt;/a&gt;, &lt;a href="https://curl.haxx.se/docs/manpage.html#-H" rel="noopener noreferrer"&gt;-H&lt;/a&gt; and &lt;a href="https://curl.haxx.se/docs/manpage.html#-d" rel="noopener noreferrer"&gt;-d&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Timestamps and Environment variables
&lt;/h3&gt;

&lt;p&gt;One thing that we wanted to add to all our logs are timestamps. Luckily our terminal comes with &lt;code&gt;date&lt;/code&gt; which can print the current date in many formats. You can refer to the &lt;a href="https://manpages.ubuntu.com/manpages/bionic/en/man1/date.1.html" rel="noopener noreferrer"&gt;Ubuntu docs on date&lt;/a&gt; for documentation on date and this &lt;a href="https://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/" rel="noopener noreferrer"&gt;article on formatting dates&lt;/a&gt; for some inspiration. Generally, you call &lt;code&gt;date&lt;/code&gt; with a single parameter that passes in what you want. &lt;code&gt;date +%s&lt;/code&gt; for example gives you the seconds since Epoch. &lt;/p&gt;

&lt;p&gt;For Elasticsearch however, we need &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html" rel="noopener noreferrer"&gt;date fields&lt;/a&gt; in milliseconds since Epoch. And our timestamp will be a field of type date so that we can filter by it. To achieve that we will add nanoseconds and then divide by 1000000. Because &lt;code&gt;date&lt;/code&gt; behaves a bit differently on different System the command below will simply fill up the specificity between seconds and milliseconds with zeros on systems that do not support milliseconds on the date command (looking at you MacOS - &lt;a href="https://apple.stackexchange.com/a/135743" rel="noopener noreferrer"&gt;Stackoverflow credit&lt;/a&gt;).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A quick foray on &lt;code&gt;$()&lt;/code&gt;: This Syntax allows us to use the return value of previous commands as input for the next one. Check out the example command line inputs below to get a feeling for what that means.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;date
echo&lt;/span&gt; &lt;span class="nv"&gt;$MY_ENV&lt;/span&gt;
&lt;span class="c"&gt;# date&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_ENV2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$MY_ENV2&lt;/span&gt;
&lt;span class="c"&gt;# Thu  9 May 2019 12:01:14 CEST&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our concrete example the nested blocks return the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 1557396103 * 1000 + N / 1000000&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;$($(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;# command not found: 1557396188&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;# 1557396266000&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we have our timestamp we want to use it in our POST request to the server. For that, we will save it in an environment variable and reference that in our request.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://jsonplaceholder.typicode.com/posts &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"timestamp": "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'"}'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let me unpack that for you: first, we assign the timestamp to an environment variable called &lt;em&gt;NOW&lt;/em&gt; than we tell our shell to execute another command without losing context through &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; here we make our POST request. The &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; approach is not necessary on all platforms but we use it here to keep the command compatible with Linux and Mac. You would usually expect to now see &lt;code&gt;${NOW}&lt;/code&gt; wherever you want to insert the environment variables value. But to get our shell to properly escape here we need to wrap our value into wrapped single quotes.&lt;/p&gt;

&lt;p&gt;A word on Operation System compatibility and escaping JSON values. Windows does not support single quotes &lt;code&gt;'&lt;/code&gt; in the command so you will have to instead use something like: &lt;code&gt;"{\"title\": \"My awesome title\"}"&lt;/code&gt;. you can also use that Syntax on Macs and Linux machines but I found the single quote approach nicer to read, even though it imposes the escaping for environment variables. Up to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elasticsearch APIs
&lt;/h2&gt;

&lt;p&gt;Elasticsearch provides great documentation on how to &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html" rel="noopener noreferrer"&gt;get started&lt;/a&gt; with understanding, creating and searching data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3zz21kxfw3bf4b8s8sc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3zz21kxfw3bf4b8s8sc.jpg" alt="Let's take a look at those internals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Indexes and Documents
&lt;/h3&gt;

&lt;p&gt;First, let's recap how Elasticsearch organizes data. The basic element in Elasticsearch is called a &lt;em&gt;Document&lt;/em&gt;. You can think of it as a single event being logged, processed and organized through Elasticsearch. They are still organized in Types which are grouped in Indexes. &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-concepts.html#_type" rel="noopener noreferrer"&gt;Types, however, have been deprecated&lt;/a&gt; in Elasticsearch 6 (at the time of writing version 7 is current) which currently results in each Index only being allowed a single Type. Indexes, on the other hand, is a collection of similar documents where you can have as many Indexes as you want. You will need those to insert and query documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Elasticsearch
&lt;/h3&gt;

&lt;p&gt;If you want to follow along examples from here run an Elasticsearch locally using Docker or replace the localhost domain with the one running your Elasticsearch. Run the command below for a Docker container with Elasticsearch and check out the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html" rel="noopener noreferrer"&gt;Elasticsearch Docker documentation&lt;/a&gt; if you run into trouble.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 9200:9200 &lt;span class="nt"&gt;-p&lt;/span&gt; 9300:9300 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"discovery.type=single-node"&lt;/span&gt; docker.elastic.co/elasticsearch/elasticsearch:7.0.0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;All following examples now refer to localhost as Elasticsearches domain so you can copy and paste them to try things out. Remember to update this if you want to use snippets in your environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inserting Documents
&lt;/h3&gt;

&lt;p&gt;You can post to any &lt;code&gt;/index/type&lt;/code&gt; to add a new Document to the specific Type of an Index. If you want to get a pretty response, add &lt;code&gt;?pretty&lt;/code&gt; to the URL.&lt;/p&gt;

&lt;p&gt;Say for example you wanted to have an Index that groups Documents related to deployments of your application. The object you initially want to log might contain the hash of your code being deployed and thus look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To now add this Document to the &lt;em&gt;deploys&lt;/em&gt; Index using &lt;em&gt;_doc&lt;/em&gt; as the standard Type (remember &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.0/getting-started-concepts.html#_type" rel="noopener noreferrer"&gt;deprecated but still needed&lt;/a&gt;) you can simply call the &lt;code&gt;/deploys/_doc&lt;/code&gt; endpoint with your data.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"localhost:9200/deploys/_doc/?pretty"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'
{
  "gitHash": "1234567"
}
'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you are not running Elasticsearch locally but on a remote server replace &lt;code&gt;localhost:9200&lt;/code&gt; above with your domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading Documents
&lt;/h3&gt;

&lt;p&gt;Now we can also get the document we just added by querying the &lt;code&gt;/index/_search&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"localhost:9200/deploys/_search/?pretty"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'
{
  "query": { "match_all": {} }
}
'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will return us all documents within the Index. Check out the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search-API.html" rel="noopener noreferrer"&gt;search API&lt;/a&gt; for more options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mappings
&lt;/h3&gt;

&lt;p&gt;We briefly touched on Mappings before when we noted that Elasticsearch expects timestamps (or date in general) to be in milliseconds since Epoch. Through Mappings, we can add fields to an Index. With this, we can control the &lt;em&gt;type&lt;/em&gt; that a field will get. While all fields default to "text" it is especially important for our timestamp that we set the field to be of type "date" before adding the first document to our Index. By doing so we ensure that we can later properly filter on our timestamp in Kibana.&lt;/p&gt;

&lt;p&gt;Sadly this means we now need to delete our Index again (and any Kibana indexes we might have created to explore our documents before the Elasticsearch Indexes).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE &lt;span class="s2"&gt;"localhost:9200/deploys"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we can add the Index back with the right mapping for our timestamp using:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"localhost:9200/deploys"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'
{
  "mappings": {
    "properties": {
      "timestamp": {
        "type": "date"
      }
    }
  }
}
'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can refer to the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.0/date.html" rel="noopener noreferrer"&gt;docs on date datatypes&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging using curl
&lt;/h2&gt;

&lt;p&gt;Now that we have the fundamentals of curl and date down, as well as an understanding of Elasticsearches API, it is finally time to get logging.&lt;/p&gt;

&lt;p&gt;We already locked at sending a message containing a timestamp via a POST request. With our knowledge of the Elasticsearch API, we can now log an event with a timestamp.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:9200/deploys/_doc"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'
  { 
    "timestamp": "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'"
  }
'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the above, you should have a solid understanding of what is happening here. Below we will dive into a more complex example with once more an explanation of what is going on.&lt;/p&gt;

&lt;h3&gt;
  
  
  A practical example
&lt;/h3&gt;

&lt;p&gt;Above I used an example of logging something containing a git hash related to a deployment. That, in fact, was my use case to dive into this. Let me present to you my solution to log the currently deployed git hash from our CI Server to Elasticsearch. The following command needs to be run from within a git repository.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%s * 1000 + %-N / 1000000'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:9200/deploys/_doc"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'
  { 
    "gitHash" : "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HASH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'",
    "timestamp": "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'",
    "info": "some infos",
    "environment": "test"
  }
'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can probably figure out what's going on here. Let me just go through it to make sure we are on the same page. First, we have three commands in total, let's examine them one at a time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HASH=$(git rev-parse --short HEAD)&lt;/code&gt; assigns the short hash (first 7 symbols) of the git repository we are currently into an environment variables named &lt;em&gt;HASH&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NOW=$(($(date +'%s * 1000 + %-N / 1000000')))&lt;/code&gt; should look familiar, it gets the current timestamp in milliseconds and assigns it to a variable named &lt;em&gt;NOW&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we bring it all together in one big curl command. Let me just go over the options for this one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-H "Content-Type: application/json"&lt;/code&gt; sets a "Content-Type" header with "application/json" so that our Elasticsearch knows that it gets valid json.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-X POST "http://your.domain:9200/deploys/deploy"&lt;/code&gt; tells curl to fire a POST request and which endpoint to use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-d '{ "gitHash" : "'"${HASH}"'", "timestamp": "'"${NOW}"'", "info": "some infos", "environment": "test"}'&lt;/code&gt; finally defines the JSON data we are going to send along with our request. Here we use the variables we created to fill in the git hash as well as a timestamp. Note that once again we use &lt;code&gt;"'"&lt;/code&gt; for proper escaping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Today we learned a lot about curl, date, and Elasticsearch. We not only took a look at how these tools can be used in isolation but also how to use them together to "just quickly log something".&lt;/p&gt;

&lt;p&gt;I hope this sets you up to do great things and achieve quick wins empowered by your existing systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkovlrvan2sag2sj21z8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkovlrvan2sag2sj21z8.png"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terminal</category>
      <category>elasticsearch</category>
      <category>logging</category>
    </item>
    <item>
      <title>Utilizing ESLint rules to format JavaScrip in VS Code</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Tue, 26 Feb 2019 09:08:19 +0000</pubDate>
      <link>https://dev.to/hoverbaum/utilizing-eslint-rules-to-format-javascrip-in-vs-code-59h5</link>
      <guid>https://dev.to/hoverbaum/utilizing-eslint-rules-to-format-javascrip-in-vs-code-59h5</guid>
      <description>

&lt;p&gt;Today we will take a quick look at how to configure VS Code to automatically format your code according to your ESLint rules every time you save a file.&lt;/p&gt;

&lt;p&gt;This assumes that you already have some ESLint rules in place. If not the &lt;a href="https://github.com/airbnb/javascript"&gt;airbnb rules&lt;/a&gt; are a good place to get you started.&lt;/p&gt;

&lt;p&gt;Once you have rules setup and are hacking away in VS Code, the editor will start to complain every time you forget that "you should indent x" or whatever extraordinary rule your company might use. Especially if you are regularly engaged with different companies using different styles this becomes quite cumbersome. Luckily there are two plugins that can help us get around this.&lt;/p&gt;

&lt;p&gt;Go ahead and install &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint"&gt;ESLint&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint"&gt;Prettier&lt;/a&gt;. The first is a plugin you probably have installed already to help you see errors in your code-style. The later is a tool generally used to format code.&lt;/p&gt;

&lt;p&gt;With prettier you can achieve great things, even &lt;a href="https://prettier.io/docs/en/precommit.html"&gt;format your code on commit&lt;/a&gt; which can greatly help to streamline coding-style across the team. For today we will only use it every time we save a file in our editor. To achieve this we simply add two lines to our settings.json.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"editor.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"prettier.eslintIntegration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can find your settings.json by using &lt;code&gt;Preferences: Open User Settings&lt;/code&gt; from your Command Palette (⇧+⌘+P | ⇧+CTRL+P).&lt;/p&gt;

&lt;p&gt;And now every time you save a file Prettier will format it according to your ES Lint configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EmGaAENH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/emojies/emoji-artist-palette.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EmGaAENH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/emojies/emoji-artist-palette.png" alt="Art Palette Emoji"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Originally published on &lt;a href="https://hoverbaum.net/2019/02/22/Utilizing-ESLint-rules-to-format-JavaScript-in-VS-Code/"&gt;HoverBaum.net&lt;/a&gt;&lt;/p&gt;


</description>
      <category>javascript</category>
      <category>codeformatting</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Automate click-through testing with Puppeteer</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Tue, 29 Jan 2019 15:28:09 +0000</pubDate>
      <link>https://dev.to/hoverbaum/automate-click-through-testing-with-puppeteer-3oh0</link>
      <guid>https://dev.to/hoverbaum/automate-click-through-testing-with-puppeteer-3oh0</guid>
      <description>&lt;p&gt;Testing is one of the essential steps in software development. Yet, testing and especially manual testing that clicks through the application is a tedious job that we often shy away from. But it doesn't have to be. With great tooling coming out over the past couple of years we now have what it takes to automate click-through testing in our build process. In this post, we will look at why manual or click-through tests are important and how to turn this tedious and bothersome task into a fun one that drives our development forward.&lt;/p&gt;

&lt;p&gt;Restless readers can jump straight into the &lt;a href="https://github.com/HoverBaum/jest-puppeteer-website-testing"&gt;repo accompanying this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we need manual QA
&lt;/h2&gt;

&lt;p&gt;From a developer point of view; testing and QA, especially manual QA can seem like an unreasonable burden placed on us to hamper our development velocity. While the developer community as a whole, as well as most of the management community, are slowly coming to their senses and seeing that writing well-tested and working code that forms a great product for users is way more valuable than squeezing in one more feature, we still frown on manual QA as a roadblock.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Manual test runs often turn into roadblocks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But manual QA really isn't the issue we face. It is the way we use it. Originally manual QA comes from the realization that someone really should click through the finished website to make sure that it really works, before shipping it to customers. You can unit test the hell out of things, users and the real world can always break things. Thus we try our best to emulate real-world scenarios and users using manual QA.&lt;/p&gt;

&lt;p&gt;Somewhere in the DevOps movement we realized that shipping small pieces of code and doing so often highly reduces the number of bugs we ship, makes our companies more able to react to user needs and market requirements as well as creates a good working environment for ourselves. Too often a big pain point in achieving a high release velocity is manual QA. Because for whatever reason our organizations require a full manual QA run for each release. This tempts us to stock up on features so that QA can process as much as possible at once (if you read the Phoenix Project, this is our heat treat often). We totally ignore that this is exactly what makes it hard to track down which of the dozens of commits introduced a bug and that by doing so we usually block development for a few days just to make a single release happen. We invent complicated procedures like a week of code freeze to create a "stable testing environment" that in reality it often turns out to be anything but stable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Programming needs QA as much as any other industry does.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still, we need manual QA just like any other industry does. But it is time to shift the focus. When we look at other disciplines we will always see manual QA, it is simply reassuring to have a human control that everything is going right. They are more flexible and more like the real world users than machines will be for a long while. But do you see quality assurance happen on every single, finished piece leaving traditional production chains? Of course not, that would be such a waste of time! When we build cars we don't need to check every single one. We take out one every so often to ensure the process runs smoothly and trust that the rest work just the same.&lt;/p&gt;

&lt;p&gt;That is the mode of operation we want to achieve in software development as well. we make a release and we make dozens of them (maybe even per day). Every once in a while someone manually verifies a release and with that, we trust that the rest work just as well as this one did because we have trust in our processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building trust
&lt;/h3&gt;

&lt;p&gt;But we don't currently have that trust. Often enough companies are scared by experiences of deployments gone horribly wrong. And as a sad result they introduce cumbersome procedures meant to catch errors before they happen when in reality these processes cause the opposite and slow the company down. It is on us to rebuild this trust and to alleviate manual QA of the burden they currently carry. We can achieve this by automating the process QA manually goes through.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To go from where we are to where we want to be, which is not blocked by manual QA runs and releasing multiple times a day, we need to invest into automated testing as well as into building the trust within the organization that our code does what it's supposed to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today we will take a great step forward on this road by automating a click-through test for a website.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple web application
&lt;/h2&gt;

&lt;p&gt;For today's test run, we will look at the website below which is a simple counter application. We will be able to hit a button "Increment" to increase the count by one. Furthermore, we will provide a button to display a message that was not previously seen, just to add more dynamic behavior for us to test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pr4tzua0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/simple-counter-website.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pr4tzua0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/simple-counter-website.png" alt="Our simple counter website."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is the code for the website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Testing Page&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-family: sans-serif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Testing page&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Counter interface --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-test=&lt;/span&gt;&lt;span class="s"&gt;"button-increment"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"js-increment"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Increment&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Current count: &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-test=&lt;/span&gt;&lt;span class="s"&gt;"count-output"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"js-count"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Button to show a message --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-test=&lt;/span&gt;&lt;span class="s"&gt;"button-display"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"js-display"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Display Message&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;

    &lt;span class="c1"&gt;// --- Counter ---&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;    
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js-increment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js-count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;incrementBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;

    &lt;span class="c1"&gt;// --- Display ---&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js-display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messageContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;messageContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A message here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="nx"&gt;messageContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see we have HTML that displays the count along with some buttons. Then we added some in-line JavaScript at the bottom to implement the logic. We expect the page to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Display when we visit it.&lt;/li&gt;
&lt;li&gt;Increase the count displayed when we click the "Increment" button.&lt;/li&gt;
&lt;li&gt;Display a message dialog when pressing the "Display Message" button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traditionally we would now manually click through this. We would host the website somewhere or locally run a web server to take a look. Then we would check that it opens and go through the requirements one by one. Let us make a computer do that for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NVDkNpBF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/jest%252Bpuppeteer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NVDkNpBF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/jest%252Bpuppeteer.png" alt="Our main tools are Jest and Puppeteer."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are going to use &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;, a framework for "Delightful JavaScript testing" as our testing framework. But you might as well use anything else that provides assertions. While we are going to use Jest here we will discuss things in a way so that we can also apply them to other tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pptr.dev/"&gt;Puppeteer&lt;/a&gt; will be our tool to automate the browser and interactions with it. Written by the Chromium Team Puppeteer provides an interface to the &lt;a href="https://chromedevtools.github.io/devtools-protocol/"&gt;DevTools Protocol&lt;/a&gt; and can be used in combination with any browser supporting it. We will be using Puppeteer with the Chromium version that comes bundled with it.&lt;/p&gt;

&lt;p&gt;For this post Jest was used in version &lt;em&gt;23.6.0&lt;/em&gt; together with Puppeteer &lt;em&gt;1.11.0&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Apart from these, we will use &lt;a href="https://github.com/tapio/live-server/"&gt;live-server&lt;/a&gt; as a local web server. Using a local web server makes us independent of frameworks used. We will be able to test any website using this approach because no matter how you develop your site, be it React, Angular, Vue or some other flavor of the year framework, in the end, you will get a plain old website. And using this approach we can test them all.&lt;/p&gt;

&lt;p&gt;I also decided that I would like ES6 module syntax in my code and included Babel to use &lt;code&gt;import&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get all of that into your project &lt;code&gt;npm i --save jest puppeteer live-server&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test environment Setup
&lt;/h2&gt;

&lt;p&gt;Now that we have our toolbelt ready, it's time to dive into testing.&lt;/p&gt;

&lt;p&gt;Earlier we said that before doing a manual test run we would host the website somewhere or run a local web server, we better do that in our test-suite before running our tests as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;serverProcess&lt;/span&gt;

&lt;span class="c1"&gt;// URL that the webserver will serve our website under.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8282&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Wait until a local webserver is started to test against.&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;serverProcess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;live-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`--port=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;testUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--no-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c1"&gt;// Better wait for a first response from the server to ensure the website is available.&lt;/span&gt;
    &lt;span class="nx"&gt;serverProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slowMo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we remember the Puppeteer managed browser as well as a system process running our web server for all our tests by defining them as top-level variables. For now, we are telling Puppeteer to launch a browser that we can watch the tests being run in. We also slow this browser down using &lt;code&gt;slowMo&lt;/code&gt;, later we will make this optional but it's always nice to see things happening when first trying them out.&lt;/p&gt;

&lt;p&gt;Sadly we could not find a good web server solution that could be managed programmatically and live-server has an open issue about shutting down programmatically (see &lt;a href="https://github.com/tapio/live-server/issues/196"&gt;issue 196&lt;/a&gt;), thus we need to run it as a child process. It's basically like running the command &lt;code&gt;live-server --port=8282 --no-browser&lt;/code&gt; in another terminal before doing your tests. We keep a reference so that we can later close the server.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;beforeAll&lt;/code&gt; is a block that will execute once before any tests run. Thus it is the perfect place for our general setup. Here we use Puppeteer to launch a browser and wait for that to be done before using it to run our tests.&lt;/p&gt;

&lt;p&gt;Of cause, we can't forget to shut all of this down once all our tests are done. For that Jest provides &lt;code&gt;afterAll&lt;/code&gt; which will run once, after all, tests have finished. It is the perfect place to close our browser and web server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Cleanup everything that is still running after all tests are through.&lt;/span&gt;
  &lt;span class="nx"&gt;serverProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While we start and stop the browser and web server for all tests we want to start with a clean, new tab for each individual test. We can achieve this using &lt;code&gt;beforeEach&lt;/code&gt; and clean up in &lt;code&gt;afterEach&lt;/code&gt;. We will also add a &lt;code&gt;let page&lt;/code&gt; where we remember the browser as well so that our tests can access it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get a new page for each test so that we start fresh.&lt;/span&gt;
  &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Remember to close pages after tests.&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will now open a new page for each test and store is always in the same variable named &lt;code&gt;page&lt;/code&gt; that our tests have access to. Not that for your test-suite in your project you might or might not want to do it this way. For our website, it's nice to always start clean and we can achieve that by simply opening a new page for each test.&lt;/p&gt;

&lt;p&gt;You might have noticed the extensive use of &lt;code&gt;await&lt;/code&gt; within the code. We will continue to see that as the nature of all operations communicating with the browser are asynchronous. If you are unfamiliar with this Syntax, Mozilla has great docs on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function"&gt;async&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await"&gt;await&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the site
&lt;/h2&gt;

&lt;p&gt;Now it's finally time to write our first test!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gs8x1Lrf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/emojies/emoji-party-popper.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gs8x1Lrf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/emojies/emoji-party-popper.png" alt="Finally, time to start."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will wrap all our test for which we will use &lt;code&gt;it&lt;/code&gt; blocks with the convention of "it should..." inside a &lt;code&gt;describe&lt;/code&gt; block. As a quick recap: the idea behind this is to describe the part of an application tested and what it should do. With this we aim to get descriptive tests that precisely tell us what is broken, should they ever fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// "it" blocks go here.&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As we would do in a manual test we will first make sure that we see the website. For that, we will check that a headline is present. We could also check to see if a specific text was in the headline but for us just having any headline will be good enough.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should have a headline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screens/basicRender.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headlines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headlines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This test navigates to the page that we want to test, finds all &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; elements on it and expects to find exactly one h1 element on the page. We also decided to take a screenshot here. This can make it easier to check for what went wrong during a test or to debug your tests.&lt;/p&gt;

&lt;p&gt;Congratulations, with your first test in place, it is time to run it. For that, we will follow nodes conventions and add a test script to our &lt;code&gt;package.json&lt;/code&gt;. &lt;code&gt;"test": "jest"&lt;/code&gt; will do the trick for us. Now you can run &lt;code&gt;npm test&lt;/code&gt; and a browser will open and take a screenshot. Just make sure that you have a folder named &lt;code&gt;screens&lt;/code&gt; that screenshots can be saved into.&lt;/p&gt;

&lt;h3&gt;
  
  
  Selecting Elements
&lt;/h3&gt;

&lt;p&gt;The above verifies number &lt;em&gt;0&lt;/em&gt; from the three things we want to check during our test. Next up is the counter itself and the displayed message.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Display when we visit it.&lt;/li&gt;
&lt;li&gt;Increase the count displayed when we click the "Increment" button.&lt;/li&gt;
&lt;li&gt;Display a message dialog when pressing the "Display Message" button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the counter, we will want to check that it starts with 0 and then that it is incremented after we click a button. Checking the initial value is a simple matter of getting the contents of the &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; element that displays the count.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S_dgFJLK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/selecting-element.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S_dgFJLK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/selecting-element.png" alt="We are looking for that span that holds the count."&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should initially have a count of 0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test="count-output"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screens/initialCount.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once again we visit the page and take a screenshot, but this time we also extract the initial value of the counter. Puppeteer provides many methods to interact with pages. Here we use &lt;code&gt;$eval&lt;/code&gt; which takes an element selector that is used for a &lt;code&gt;querySelect&lt;/code&gt; run on the page and as a second argument a function that gets the found element as it's input. The function passed to &lt;code&gt;$eval&lt;/code&gt; is evaluated in the context of the page and its result becomes &lt;code&gt;$eval&lt;/code&gt;s return value. You can look up the full API in the &lt;a href="https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here we find an element identified by &lt;code&gt;[data-test="count-output"]&lt;/code&gt; and then turn it's &lt;code&gt;.innerHTML&lt;/code&gt; into an integer which we return. This exposes one crucial part of automated click-through testing. Our tests need to find the elements to interact with and run tests on. You could also solve this by searching for a certain text on the website but for sites with frequently changing text as well as for outputs we need a different way to select them.&lt;/p&gt;

&lt;p&gt;A great approach is custom &lt;code&gt;data-*&lt;/code&gt; attributes. Custom data attributes don't interfere with the rest of how web development works. They are here for exactly the case of an element needing some specific information that otherwise can't be provided.&lt;/p&gt;

&lt;p&gt;The two alternatives would be classes and ids. While classes are meant to not be unique and thus are unusable to select exactly one thing, ids are meant for something else already. They are probably already used and are something you don't want to couple to your testing procedures, as it would block you from fully utilizing them for application development. Thus we use data attributes here to identify elements of interest for testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interacting with Element
&lt;/h3&gt;

&lt;p&gt;Once we can find elements on the page, we can also interact with them and for example, click a button to increment our counter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m8X4A88K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/increment.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m8X4A88K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/increment.gif" alt='Clicking on "Increment" will increase the count.'&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should increment on click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test="button-increment"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test="count-output"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initialCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;incrementBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test="count-output"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screens/incrementedCount.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we get the initial count value and validate our expectation that clicking the "increment" button will increment the value by one. Using &lt;code&gt;page.$&lt;/code&gt; we can get an &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v1.11.0&amp;amp;show=api-class-elementhandle"&gt;ElementHandle&lt;/a&gt; which provides us with certain convenience functions to interact with the selected element, like &lt;code&gt;.click()&lt;/code&gt; to click it.&lt;/p&gt;

&lt;p&gt;Within the &lt;a href="https://github.com/HoverBaum/jest-puppeteer-website-testing"&gt;repo for this blog post&lt;/a&gt; you can find another test for the counter that increments multiple times.&lt;/p&gt;

&lt;p&gt;With that, we can check off another item from our testing list.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Display when we visit it.&lt;/li&gt;
&lt;li&gt;✅ Increase the count displayed when we click the "Increment" button.&lt;/li&gt;
&lt;li&gt;Display a message dialog when pressing the "Display Message" button.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Dynamic DOM content
&lt;/h3&gt;

&lt;p&gt;All we have left now, are elements that dynamically get added to the page and we can test those too, like the message in our example website. To do so we simply execute the action that will cause the element to be added to the page and then look for it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jd_6q_Ea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/display-message.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jd_6q_Ea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/display-message.gif" alt='When clicking "Display Message" a new message is shown.'&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should display a message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test="button-display"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screens/messageDisplay.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test="display"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This time we don't use Puppeteers build in support for click events but a piece of JavaScript evaluated in the context of our page to click the button &lt;code&gt;e =&amp;gt; e.click()&lt;/code&gt;. In the same way, you can pass any code to be evaluated in the context of the page.&lt;/p&gt;

&lt;p&gt;That already gets us pretty far in testing websites. Don't forget that when testing your actual site you can probably take a lot of shortcuts by sending requests to your backend directly instead of doing everything all the time through your UI. Take a moment to reflect on what you want to test before you do so and do your best to de-couple tests from one another. One test failing shouldn't make all others fail as well. Ideally, you want one test to fail on its own to ease finding the issue. Following this, it's often good to setup (or seed) data and state for a test that could come from a backend by making calls to it directly (or using a mock backend). That way you can verify displaying a list even while creating items for it is currently failing its tests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Display when we visit it.&lt;/li&gt;
&lt;li&gt;✅ Increase the count displayed when we click the "Increment" button.&lt;/li&gt;
&lt;li&gt;✅ Display a message dialog when pressing the "Display Message" button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congratulations, all tests are green!&lt;/p&gt;

&lt;h3&gt;
  
  
  Making visualization optional
&lt;/h3&gt;

&lt;p&gt;Earlier we hardcoded displaying a browser with a delay to watch our tests happen. Since we don't always want to do that, let's change it up to depend on an environment variable &lt;code&gt;SHOW_BROWSER&lt;/code&gt;. Cases were we might not want to get the visual especially include pipelines for build and deploy of out application which usually runs on systems where we don't care about visualization but speed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browserConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHOW_BROWSER&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;slowMo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browserConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will simply check whether or not the environment contains a variable named &lt;code&gt;SHOW_BROWSER&lt;/code&gt;and depending on that show the browser or not. To now see the browser you could run &lt;code&gt;env SHOW_BROWSER=t npm test&lt;/code&gt; to run tests with a browser. You can replace &lt;code&gt;t&lt;/code&gt; with anything you want, as long as &lt;code&gt;SHOW_BROWSER&lt;/code&gt; has any value at all. You could even turn this into an npm script &lt;code&gt;"test:visual": "env SHOW_BROWSER=t npm test&lt;/code&gt; and run &lt;code&gt;npm run test:visual&lt;/code&gt; to use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations and good tests
&lt;/h3&gt;

&lt;p&gt;As with every approach, this automated click-through testing isn't omnipotent, it comes with some limitations. For instance that Puppeteer basically only runs with Chrome. But as we discussed earlier automated click-through testing shouldn't replace manual QA runs but help them. Once we achieve fast development iterations which will give us fewer bugs, testing in one browser for each build will be just fine, as long as we do periodic checks in all browsers. Always assuming that developers check their code in multiple browsers anyways 😉&lt;/p&gt;

&lt;p&gt;You might find it helpful to introduce timeouts in your tests. You will notice when to use them, as tests will fail when the default timeout isn't long enough simply hand your desired timeout as a second argument to &lt;code&gt;it()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lb1ugFn1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/real-life-party-popper.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lb1ugFn1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/puppeteer-testing/real-life-party-popper.png" alt="We made it"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our goal is reached, we started testing a website using Jest and Puppeteer. We took a look at where automated click-through testing fits within the greater context of software development and then went on to write our first test cases. Check out the &lt;a href="https://github.com/HoverBaum/jest-puppeteer-website-testing"&gt;repo with our finished code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now go out and automate all your tests 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ijHtkAVy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/emojies/emoji-tree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ijHtkAVy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.googleapis.com/hoverbaum-blog-assets/emojies/emoji-tree.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next episode: Automated testing using Cypress to visually debug automated tests (coming soon)&lt;/p&gt;




&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/HoverBaum/jest-puppeteer-website-testing"&gt;repo accompanying this post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/"&gt;https://jestjs.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pptr.dev/"&gt;https://pptr.dev/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>puppeteer</category>
      <category>testing</category>
      <category>automation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What are some great blogs to contribute to / collaborate with for webdevs?</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Wed, 14 Nov 2018 09:58:36 +0000</pubDate>
      <link>https://dev.to/hoverbaum/what-are-some-great-blogs-to-contribute-to--collaborate-with-for-webdevs-4no2</link>
      <guid>https://dev.to/hoverbaum/what-are-some-great-blogs-to-contribute-to--collaborate-with-for-webdevs-4no2</guid>
      <description>&lt;p&gt;Recently collaborations and contributions to and with blogs have motivated me a great deal to pick up my blogging game. I wrote an article for Contentful about &lt;a href="https://www.contentful.com/blog/2018/05/02/how-i-fell-in-love-with-an-api-first-cms/"&gt;API first CMS&lt;/a&gt;, produced a piece for Netlights make it new about &lt;a href="https://makeitnew.io/polling-using-rxjs-8347d05e9104"&gt;polling in RxJS&lt;/a&gt; and then some.&lt;/p&gt;

&lt;p&gt;Now I am on the look out for more collaboration / contribution programs out there on webdev related blogs, so my question to you today is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  Which webdev related blogs out there are looking for contributors?
&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you worked with / for a blog, feel free to share you experience.&lt;br&gt;
If you are looking for contributors to your blog, this is your time to advertise.&lt;br&gt;
If you are searching for more places to post as well, this is your opportunity to get inspired.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Go based proxies for developing mobile websites on corporate WiFis</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Tue, 02 Oct 2018 14:56:56 +0000</pubDate>
      <link>https://dev.to/hoverbaum/go-based-proxies-for-developing-mobile-websites-on-corporate-wifis-5gjo</link>
      <guid>https://dev.to/hoverbaum/go-based-proxies-for-developing-mobile-websites-on-corporate-wifis-5gjo</guid>
      <description>&lt;p&gt;You might know this scenario:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We would really love to debug the web-app on an actual phone but they way our corporations WiFi is set up just won't allow it…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3xtYq4M---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1d2vwn7kap3i0brauahw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3xtYq4M---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1d2vwn7kap3i0brauahw.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
When networks become a show stopped for development.



&lt;p&gt;If you do, stay tuned because in this blog post we will examine how we as developers can handle tightly secured WiFi Networks and still get all the connectivity we need.&lt;/p&gt;

&lt;p&gt;We will set out by exploring a detailed, real world scenario and then explore a solution using hotspots and Go based proxies.&lt;/p&gt;

&lt;h2&gt;
  
  
  An exemplary situation
&lt;/h2&gt;

&lt;p&gt;Recently we were developing a Capacitor based hybrid web application. To really debug that you run a webserver on your laptop and connect your phone via WiFi. Only trouble: on our corporations WiFi phones get isolated for security reasons.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Phones get isolated for security reasons!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Luckily the isolation problem can quite easily be solved by connecting your laptop with the internet via ethernet and using the Laptops WiFi to opening up a hotspot. Just make sure to secure that with a strong password!&lt;/p&gt;

&lt;p&gt;Sadly, that cut our laptops off from the internal networks which we had to reach again by connecting our laptops to the companies VPN (jup, ethernet only went to the public internet). While the hotspot handed the internet connection through to the phone it doesn't do the same magic for the VPN connection. So, how do we now get this connection through to our phones?&lt;/p&gt;

&lt;p&gt;We need the internal network because the phone needs to connect to the deployed test backend, available on the companies internal network that our laptops connect to using VPN.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AVNagqLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7u7zaxhfkhckw5i43zbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AVNagqLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7u7zaxhfkhckw5i43zbn.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Proxies to the rescue
&lt;/h2&gt;

&lt;p&gt;Handing traffic around on a network is something we as web developers are familiar with. Often time you need an NGINX to hand requests from an entry URL to the right internal service running on a different machine (or to dockerize a web app). That is called a proxy.&lt;/p&gt;

&lt;p&gt;As just mentioned we could setup an NGINX or similar on our laptops and use that as a proxy. And this does work. However it makes it more difficult to check the solution into version control. A better maintainable version is to write a simple proxy in your language of choice. We choose Go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/elazarl/goproxy"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;goproxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewProxyHttpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Verbose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":9090"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is all the code you need to now run go run proxy.go and start a proxy on your machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EvBz5E_o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cccgwvbe3oro7kk2fib4.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EvBz5E_o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cccgwvbe3oro7kk2fib4.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
Green light for our traffic thanks to proxies.



&lt;p&gt;On your phone you now need to tell it to &lt;strong&gt;not only&lt;/strong&gt; connect to your laptops hotspot but also use a proxy. For that you likely need your laptops IP within the hotspot (it will likely always assign itself the same one) as well as the port (here: &lt;em&gt;9090&lt;/em&gt;) to set that up.&lt;/p&gt;

&lt;p&gt;With this you are now ready to finally do development on your phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Today we looked at how a simple proxy in Go can help us to develop in tightly secured corporate networks. With just a few lines of code and a bit of setup you can get going.&lt;/p&gt;

&lt;p&gt;Now, go out and build something awesome ✨&lt;/p&gt;




&lt;h3&gt;
  
  
  Reflection
&lt;/h3&gt;

&lt;p&gt;There is one obvious question here: &lt;em&gt;Is this a good idea??!?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I am so glad that you asked. And in truth I am not sure. It surely feels like a hack and it is one. We are basically getting around a security measurement established for a good reason. I would encourage you to see the practices here as a short term solution and to pick up a discussion with you management and IT about a long term solution.&lt;/p&gt;

&lt;p&gt;Any comments and ideas for better solutions are highly welcome and appreciated.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Dockerizing modern web apps</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Wed, 19 Sep 2018 08:09:31 +0000</pubDate>
      <link>https://dev.to/hoverbaum/dockerizing-spas-2lc9</link>
      <guid>https://dev.to/hoverbaum/dockerizing-spas-2lc9</guid>
      <description>&lt;p&gt;Most websites these days are Single Page Applications (SPA for short) where a single entry file handles all routes that a user might visit. Swept up in the ongoing trend of hosting in the cloud you might find yourself needing to “dockerize” your SPA. That is to say wrap it inside a Docker image and run it as a container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjse2w2w9dmhimuf3cei8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjse2w2w9dmhimuf3cei8.jpg" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;These days everything is shipped in containers, even software.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;In this post we will explore how we can get that done. We are going to build a simple SPA that just tells the user which route of our website they are currently visiting. That means you should be able to visit not only &lt;code&gt;/&lt;/code&gt; but also any route you might think of, such as &lt;code&gt;/unicorn&lt;/code&gt; or &lt;code&gt;/rainbow&lt;/code&gt;. This SPA will be a super simple and hand made one but you can see it as representative for any complex React, Angular or Vue app you might want to deploy. Finally we are going to build our SPA into a Docker image and deploy that as a container.&lt;/p&gt;

&lt;p&gt;We will run through all the basics of what we are doing. So whether you are seasoned with Docker and just can’t get that SPA to run on your cluster or you are a great web developer tasked to do this Docker thing, this post is for you.&lt;/p&gt;
&lt;h3&gt;
  
  
  The website
&lt;/h3&gt;

&lt;p&gt;Our website will be super simple. Just a headline and a paragraph telling our user where they are by checking &lt;code&gt;window.location&lt;/code&gt;. Below that we will offer links  to navigate to a few routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Simple SPA&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-family: sans-serif;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to a simple SPA&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;You are on: &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"locationSpan"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;You could go to:&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/unicorn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Unicorn&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/rainbow"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rainbow&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#locationSpan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this locally you could initialize a package.json, install &lt;a href="https://www.npmjs.com/package/live-server" rel="noopener noreferrer"&gt;live-server&lt;/a&gt; and add a start script to your package.json &lt;code&gt;"start": "live-server --entry-file=index.html"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; live-server
&lt;span class="c"&gt;# Now add the script o your package.json before running:&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and click on a few of the links to move around or enter another path into your navigation bar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tdoxad7v31qwthcdxyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tdoxad7v31qwthcdxyj.png" width="480" height="213"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;Our super simply SPA telling a user where they are and allowing them to&lt;br&gt;
navigate.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;You might already notice that we are doing something to make our SPA work as we expect it to. We need to tell live-server to serve our index.html on all routes that it can't find a file for. We do this using &lt;code&gt;--entry-file=index.html&lt;/code&gt;. Feel free to try running the  ive-server without the — entry-file parameter and see what happens in that case.&lt;/p&gt;

&lt;p&gt;Keep this flag in mind as we will need to do something equivalent for our dockerization.&lt;/p&gt;

&lt;p&gt;See the website in action at &lt;a href="https://dockerized-spa.now.sh/" rel="noopener noreferrer"&gt;dockerized-spa.now.sh&lt;/a&gt;. (Already dockerized and hosted on Now.sh.)&lt;/p&gt;
&lt;h3&gt;
  
  
  Naive Docker attempt
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; is a system to create images which can then be run as containers. You can think of docker images as super lightweight Virtual Machines that can be run on many platforms (in this image containers are running VMs). The awesomeness of all this is that once you build a docker image and run it somewhere you will get the same thing running everywhere. Once we manage to build a Docker image locally that we can run as a container successfully we know that it will also run on AWS, GCP, Portainer or whatever else your company might be using.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker solves the infamous “it works on my machine” problem. Containers run the&lt;br&gt;
same wherever you start them!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First you need a Dockerfile. Let us start out with a &lt;em&gt;Naive-Dockerfile&lt;/em&gt;. Within it we will define the steps needed to create an image. In our case we just want an image that can serve websites and hold a copy of our Single Page Application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM nginx

COPY index.html /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we base our image on the &lt;code&gt;nginx&lt;/code&gt; image. &lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt; is a simple and lightweight webserver that can serve our &lt;em&gt;index.html&lt;/em&gt;. To enable this we copy our website into the folder that NGINX will be serving. Now that we have this, let’s build our image and run it.&lt;/p&gt;

&lt;p&gt;Above we first create the Docker image using &lt;a href="https://docs.docker.com/engine/reference/commandline/build/" rel="noopener noreferrer"&gt;Docker build&lt;/a&gt;. Using the &lt;code&gt;-f&lt;/code&gt; Flag we tell Docker which “Dockerfile” to use, which files holdes the configuration to build our image. The &lt;code&gt;-t&lt;/code&gt; flag “tags” our Docker image. It gives it a name we can use to run it.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://docs.docker.com/engine/reference/commandline/run/" rel="noopener noreferrer"&gt;Docker run&lt;/a&gt; we then start the image as a container. By using &lt;code&gt;-p&lt;/code&gt; we can specify a mapping for exposed ports, in this case that we want to reach the exposed port &lt;em&gt;80&lt;/em&gt; at port &lt;em&gt;8888&lt;/em&gt; on our local machines. So go ahead and open &lt;a href="http://localhost:8888/daf" rel="noopener noreferrer"&gt;http://localhost:8888/&lt;/a&gt; to checkout what we got.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd82zjbpbygc0d90zg1fy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd82zjbpbygc0d90zg1fy.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2eta1uw8h6wrtua2u104.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2eta1uw8h6wrtua2u104.png" width="120" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thats not how we imagined this would go…&lt;/p&gt;

&lt;p&gt;With our “naive Docker” approach all but our entry route &lt;code&gt;/&lt;/code&gt; display NGINXs 404 page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi &lt;span class="nt"&gt;-f&lt;/span&gt; docker-spa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets clean up after ourselves using the &lt;a href="https://docs.docker.com/engine/reference/commandline/rmi/" rel="noopener noreferrer"&gt;Dockerrmi&lt;/a&gt; command and &lt;code&gt;-f&lt;/code&gt; forcing it to remove our created image. Time to pull up our sleeves and get this fully working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Empowering SPA capabilities
&lt;/h3&gt;

&lt;p&gt;Remember how up above we needed to pass &lt;code&gt;--entry-file=index.html&lt;/code&gt; to live-server in order for it to serve our &lt;em&gt;index.html&lt;/em&gt; file for each route where it couldn’t find a file to serve? What we need now is the equivalent of this parameter for NGINX.&lt;/p&gt;

&lt;p&gt;For that we will use an NGINX configuration and add it to our Docker image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen   80;
    listen   [::]:80 default ipv6only=on;

    root /usr/share/nginx/html;
    index index.html;

    server_name _; # all hostnames

    location / {
        try_files $uri /index.html;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above config we tell NGINX to accept traffic on port &lt;em&gt;80&lt;/em&gt; no matter the domain. Then we tell it to resolve paths ending in a slash as &lt;code&gt;index.html&lt;/code&gt; and finally specify that for all routes it should check if there is a file and otherwise serve the index file.&lt;/p&gt;

&lt;p&gt;After adding the above to our project we can now also COPY it into our Docker image to tell NGINX to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM nginx

COPY nginx.config /etc/nginx/conf.d/default.conf
COPY index.html /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And once again you can test this locally using the same commands as above with our new &lt;em&gt;Dockerfile&lt;/em&gt;. Remember to point your browser to &lt;a href="http://localhost:8888/unicorn" rel="noopener noreferrer"&gt;http://localhost:8888/unicorn&lt;/a&gt; to see it in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi &lt;span class="nt"&gt;-f&lt;/span&gt; docker-spa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clicking through the links you will see that now it does work. Each route you visit is now served by our Single Page Application we build at the beginning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx60oqj5pejxtfodpes5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx60oqj5pejxtfodpes5.png" width="465" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehsl2exfja502kpvjktw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehsl2exfja502kpvjktw.png" width="120" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus level — compiling your SPA inside Docker
&lt;/h3&gt;

&lt;p&gt;Chances are your application isn’t just a single, static HTML file. In fact you probably have a quite modern and sophisticated toolchain involving TypeScript, Webpack, Parcel or similar to build your application. You could easily do this build step inside a Docker file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ---- Base Node ----
FROM node:alpine AS base
# Copy project file
COPY . .
# Build project
RUN npm run build

# ---- Prod ----
FROM nginx
# Copy needed files
COPY nginx.config /etc/nginx/conf.d/default.conf
COPY --from=base build /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above uses a &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="noopener noreferrer"&gt;multistage Docker build&lt;/a&gt;. It first builds an image based on Node in which we run our build script and then builds an image as we did before but copying the compiled application form the&lt;br&gt;
build image instead of our local file system where we run &lt;code&gt;docker run&lt;/code&gt; from.&lt;/p&gt;

&lt;p&gt;We can illustrate the usage of this with our simple SPA by adding a build script to our &lt;em&gt;package.json&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"build"&lt;/span&gt;: &lt;span class="s2"&gt;"rm -rf build &amp;amp;&amp;amp; mkdir build &amp;amp;&amp;amp; cp index.html build/index.html"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and try it out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi &lt;span class="nt"&gt;-f&lt;/span&gt; docker-spa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at all of this together in this &lt;a href="https://github.com/HoverBaum/super-simple-dockerized-spa" rel="noopener noreferrer"&gt;repo illustrating how to host SPAs using Docker&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a day
&lt;/h3&gt;

&lt;p&gt;We built a Single Page Application, tested it locally, packaged it in a Docker image and finally enabled the Docker image to act as an SPA should, answering onall routes.&lt;/p&gt;

&lt;p&gt;I hope you learned a thing or two today. Now, be brave, be bold, go out, apply your new found knowledge and host your application using Docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe4fbvvnh2jzja585sm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe4fbvvnh2jzja585sm2.png" width="120" height="120"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/HoverBaum/super-simple-dockerized-spa" rel="noopener noreferrer"&gt;Repo with code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dockerized-spa.now.sh/" rel="noopener noreferrer"&gt;Live Demo on Now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to add code highlighting to your Dev.to posts.</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Mon, 10 Sep 2018 12:21:34 +0000</pubDate>
      <link>https://dev.to/hoverbaum/how-to-add-code-highlighting-to-your-devto-posts-2lp6</link>
      <guid>https://dev.to/hoverbaum/how-to-add-code-highlighting-to-your-devto-posts-2lp6</guid>
      <description>&lt;p&gt;The simple truth of the matter is that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;turorialFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does look way nicer than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const turorialFunction = (name) =&amp;gt; {
  console.log(`Hello ${name}`)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when writing a post here on Dev.to.&lt;/p&gt;



&lt;h2&gt;
  
  
  How to do it
&lt;/h2&gt;

&lt;p&gt;Dev.tos posts are based on Markdown. Within Markdown we can use &lt;em&gt;identation&lt;/em&gt; or so called &lt;em&gt;Code Blocks&lt;/em&gt; to specify sections of code. The later ones are indicated using &lt;code&gt;```&lt;/code&gt;. Read more about this in &lt;a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet#code-and-syntax-highlighting" rel="noopener noreferrer"&gt;this cheatsheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using the three &lt;em&gt;`&lt;/em&gt; variant we can also specify a language for the code block. A lot of tooling build on top of Markdown utilized this characteristic to implement richer features. But the simplest of them is code highlighting. The above nicely colored code snippet is achieved by starting the code block with &lt;code&gt;```javascript&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The full example for the above would be:&lt;/p&gt;

&lt;pre&gt;
```javascript
const turorialFunction = (name) =&amp;gt; {
  console.log(`Hello ${name}`)
}
```
&lt;/pre&gt;

&lt;p&gt;And if you are now wondering how the hell I got that to display:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;pre&amp;gt;
```javascript
const turorialFunction = (name) =&amp;gt; {
  console.log(`Hello ${name}`)
}
```
&amp;lt;/pre&amp;gt;
&lt;/pre&gt;

&lt;p&gt;and the inline code is: &lt;code&gt;&amp;lt;code&amp;gt;&amp;amp;#96;&amp;amp;#96;&amp;amp;#96;&amp;lt;/code&amp;gt;&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;Here is where my explanations stop and your colorful posts start.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F324caivekfv46222b3ln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F324caivekfv46222b3ln.png" width="160" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;The list of supported languages is impressive, though not all encompassing (check comments).&lt;/small&gt;&lt;/p&gt;

</description>
      <category>explainlikeimfive</category>
      <category>postwriting</category>
      <category>markdown</category>
      <category>codeception</category>
    </item>
    <item>
      <title>Polling using RxJS</title>
      <dc:creator>Hendrik</dc:creator>
      <pubDate>Mon, 10 Sep 2018 09:12:27 +0000</pubDate>
      <link>https://dev.to/hoverbaum/polling-using-rxjs-3c3i</link>
      <guid>https://dev.to/hoverbaum/polling-using-rxjs-3c3i</guid>
      <description>&lt;p&gt;As observables are gaining more and more popularity in JavaScript we are looking to accomplish our everyday tasks using them and evaluating whether they are really worth all the hype. One task you might find yourself doing is polling the backend to know whether a longer running task has completed.&lt;/p&gt;

&lt;p&gt;We will walk through an example of such a scenario and implement a solution using RxJS. On our way we will learn some basic operators for RxJS, and a few techniques as well as how to avoid a pitfall or two. At the end I will present a real-world example to show you how to implement what we learned in a specific scenario.&lt;/p&gt;

&lt;p&gt;You should bring a basic understanding of Streams / &lt;a href="http://reactivex.io/documentation/observable.html" rel="noopener noreferrer"&gt;Observables&lt;/a&gt; as well as solid foundations in JavaScript to enjoy this post. For the rest of this post I will treat Stream and Observable as interchangeable words for the same thing. While we will cover a lot of basic things they will mostly be RxJS specifics and less the basics about Streams. Should you be looking for a general introduction consider the gist title “&lt;a href="https://gist.github.com/staltz/868e7e9bc2a7b8c1f754" rel="noopener noreferrer"&gt;The introduction to Reactive Programming you’ve been missing&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;Code for this post was tested using RxJS 6.2.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;Lets say we have a backend that exposes an endpoint /tasks/[taskId] which you can query to learn about the status of a specific task. It’s returning an object like such:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  // Whether the task is still running
  processing: boolean;
  // A unique ID for this task
  taskId: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once we start polling we want to get the current state of this task twice a second and stop polling once &lt;code&gt;processing === false&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programmatic solution
&lt;/h2&gt;

&lt;p&gt;To start out we are going to look at a programmatic solution for this problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;pollUntilTaskFinished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;pollUntilTaskFinished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;pollingFinishedFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we simply invoke a new timeout every time the backend is still processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using RxJS
&lt;/h2&gt;

&lt;p&gt;Now we are going to achieve the same behavior using RxJS.&lt;/p&gt;

&lt;p&gt;First of all we need something to emit an event every &lt;em&gt;x&lt;/em&gt; time. RxJS provides two function for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;interval&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;timer&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While &lt;em&gt;interval&lt;/em&gt; emits the first event after a given time and then continuously with the same interval, &lt;em&gt;timer&lt;/em&gt; starts after a given time to emit events every &lt;em&gt;x&lt;/em&gt; time. For our two updates per second we can start by using timer(0, 500). This will start firing events right of the bat and after that twice a second.&lt;/p&gt;

&lt;p&gt;Let’s first see that in action by logging something to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;polling&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your console print “polling” twice a second now.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Take care to import from the correct package (rxjs or rxjs/operators). Sadly RxJS documentation might not be up to speed with the version you are using.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next we want to turns these “ticks” into requests to our backend. We are going to use the same fetch from above but this time &lt;strong&gt;turn the promise into an Observable&lt;/strong&gt;. Luckily RxJS provides convenient functions for this, namely &lt;em&gt;from&lt;/em&gt;. Using this we can now create an Observable (or stream) representing a request to the backend on every tick and continue working with that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.pipe&lt;/strong&gt; is RxJS’s way to specify that a transform will now happen on the stream. By extracting operators into their own imports RxJS enables better treeshaking than an overloaded Observable implementation ever could, see &lt;a href="https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md" rel="noopener noreferrer"&gt;this explanation&lt;/a&gt; for more context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  Pipe is RxJS’s wrapper around transforms that are applied to a Stream of event.
&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;p&gt;The result of this will be a &lt;strong&gt;stream of streams&lt;/strong&gt;. Each emitted value will itself be an observable. To manage the mayhem we can pipe it through &lt;em&gt;concatMap&lt;/em&gt; which will flatten all the Streams into a single one containing the nested values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;concatMap&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finish polling
&lt;/h2&gt;

&lt;p&gt;Finally we really care about getting an event that tells us the backend finished processing, that our polling is done. We can achieve this by filtering for events where the backend is no longer processing and only taking the first one of those. By using &lt;em&gt;take(1)&lt;/em&gt; we specify that we only care about a single (the first) event telling us that processing is finished. &lt;strong&gt;This will stop our polling once the backend is done processing the task.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;take&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backendData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;backendData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Now it’s time to put it all together and replace our function from up above using the new RxJS-based code. The final touch is to use &lt;em&gt;subscribe&lt;/em&gt; at the end of our Stream to work with the single event our Stream emits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;take&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nf"&gt;pollUntilTaskFinished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backendData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;backendData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;pollingFinishedFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might not want to call a function once you are done though but use the output of your Observable to render your UI. Through the use of merge, which merges two streams together we can map our polling onto two states and use the output directly for our UI.&lt;/p&gt;

&lt;p&gt;To achieve this we will merge our stream from above together with an initial value that we turn into a Stream using &lt;em&gt;of&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;take&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadingEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backendData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;backendData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⏳&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we map the response from our backend onto the processing attribute using &lt;em&gt;map&lt;/em&gt;, we can in turn map the result onto an emoji to display to our users.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real world example
&lt;/h2&gt;

&lt;p&gt;Theory is always nice but the real world usually poses a different challenge from a nicely written and contained tutorial. Let me present you with the solution to a problem we faced when building our knowledge about polling using RxJS.&lt;/p&gt;

&lt;p&gt;The situation: We have an Angular application for which we use &lt;a href="https://ngxs.gitbook.io/ngxs" rel="noopener noreferrer"&gt;NGXS&lt;/a&gt; as a state manager. Similar to Redux it uses Actions to represent events changing the state.&lt;/p&gt;

&lt;p&gt;As it turns out NGXS provides a stream of all Actions dispatched as an Observable we can hook into. Here is our final solution to poll the backend for processing states for each &lt;em&gt;Document *that&lt;/em&gt; *gets added to the state and update the state once the backend is done processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions$&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ofActionSuccessful&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AddDocument&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AddDocument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;externalProcessingState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentStates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AddDocument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mergeMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;polling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startingOffset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;polling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="c1"&gt;// Here we want a new stream per document add.&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/documents/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;polledDocument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;polledDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;externalProcessingState&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentStates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;polledDocument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AddDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;polledDocument&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;environment&lt;/strong&gt; is an Angular environment providing configuration for our application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;backend&lt;/strong&gt; is a Service providing connection to our backend. It inserts a few required headers and such.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This uses TypeScript so polledDocument: Document describes a variable named “polledDocument” which follows the type “Document”.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A tricky thing here is that we need to create a new “polling Stream” per document getting added to our state. At first we tried pulling logic into a single level but that ended with us only being able to poll for a single document per page load as take(1) would block the Stream for all future pollings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Today we built our first polling logic using RxJS learning about this great library along the way. We also took a look at a real world example and saw how expressive it can make our code.&lt;/p&gt;

&lt;p&gt;Now, go out and apply your newfound knowledge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AcJGdKu6pdTG8teb0" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AcJGdKu6pdTG8teb0"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Other great resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.strongbrew.io/rxjs-polling/" rel="noopener noreferrer"&gt;https://blog.strongbrew.io/rxjs-polling/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sitepoint.com/angular-rxjs-create-api-service-rest-backend/" rel="noopener noreferrer"&gt;https://www.sitepoint.com/angular-rxjs-create-api-service-rest-backend/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.learnrxjs.io/recipes/http-polling.html" rel="noopener noreferrer"&gt;https://www.learnrxjs.io/recipes/http-polling.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Originally published on &lt;a href="https://makeitnew.io/polling-using-rxjs-8347d05e9104" rel="noopener noreferrer"&gt;makeitnew.io&lt;/a&gt; on August 30 2018.&lt;/p&gt;

</description>
      <category>rxjs</category>
      <category>javascript</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
