<?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>Forem: Albert Alises</title>
    <description>The latest articles on Forem by Albert Alises (@aalises).</description>
    <link>https://forem.com/aalises</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%2F100924%2F9b06b4a3-6b24-4950-9d34-bdb57c20b99d.jpeg</url>
      <title>Forem: Albert Alises</title>
      <link>https://forem.com/aalises</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aalises"/>
    <language>en</language>
    <item>
      <title>Create an Interactive Bash Command for searching and checking out Git Branches on the Terminal using fzf</title>
      <dc:creator>Albert Alises</dc:creator>
      <pubDate>Sat, 25 Apr 2020 15:32:49 +0000</pubDate>
      <link>https://forem.com/aalises/create-an-interactive-bash-command-for-searching-and-checking-out-git-branches-on-the-terminal-using-fzf-51ok</link>
      <guid>https://forem.com/aalises/create-an-interactive-bash-command-for-searching-and-checking-out-git-branches-on-the-terminal-using-fzf-51ok</guid>
      <description>&lt;p&gt;&lt;em&gt;✏️ Cover art by Michael Rayback on &lt;a href="https://dribbble.com/shots/5167056-Two-Branches" rel="noopener noreferrer"&gt;Dribble&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Git is an essential and powerful tool for any software developer. As part of your developer workflow, you might find yourself juggling between different projects and features all the time.&lt;/p&gt;

&lt;p&gt;Given so, it's very hard to keep track of all the names of the branches between projects, especially when coming back to it after some time. So one common cumbersome procedure is looking up the name of the branch you want to get on that specific project and &lt;code&gt;git checkout&lt;/code&gt; to it.&lt;/p&gt;

&lt;p&gt;To achieve so, you first need to get all the branch names of the current project. Fortunately for us, &lt;code&gt;git&lt;/code&gt; has a command for this, &lt;a href="https://git-scm.com/docs/git-for-each-ref" rel="noopener noreferrer"&gt;&lt;strong&gt;for-each-ref&lt;/strong&gt;&lt;/a&gt;. What it does is iterate over all the references,  which is a readable name that points to the SHA-1 ids of git artifacts (e.g commits), given a pattern. For example, getting &lt;code&gt;refs/heads&lt;/code&gt; returns a list of all the references of the head commits for each branch.&lt;/p&gt;

&lt;p&gt;You can iterate on these references to print all the branches on the terminal by using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="nt"&gt;-each-ref&lt;/span&gt; &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'authordate:iso8601'&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'%(authordate:relative)%09%(refname:short)'&lt;/span&gt; refs/heads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Figure 1: command for showing all the git branches ordered by descendent author-date




&lt;p&gt;Of course, you can format and sort the results however you like. The next step would be to be able to interact with that list of branches, like selecting one, copying one, searching, and/or filtering the results. &lt;/p&gt;

&lt;p&gt;A pretty useful command for that is the well-known &lt;code&gt;grep&lt;/code&gt;, which allows us to find patterns in the results. For example, we could chain the output of the branches command to &lt;code&gt;grep pattern&lt;/code&gt; to search for branches that match the pattern.&lt;/p&gt;

&lt;p&gt;I want to only see the branches which are prefixed with &lt;code&gt;fix&lt;/code&gt; because I want to come back to fix that pesky bug? no problem, &lt;code&gt;git branches | grep fix&lt;/code&gt; would do the filtering (pst, adding an &lt;a href="https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases" rel="noopener noreferrer"&gt;alias&lt;/a&gt; to the branches command is pretty useful). Then you can copy the branch name and check out to it&lt;/p&gt;

&lt;p&gt;... But what if we want to interactively filter the branches and navigate through them, as well as binding keyboard commands to actions? (to, for example, check out to a branch using enter!)... you can easily do that with &lt;code&gt;Fzf&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Introducing Fzf
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;Fzf&lt;/a&gt; is a &lt;code&gt;grep&lt;/code&gt; on steroids. According to the description, it is a &lt;code&gt;general-purpose command-line fuzzy finder&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/junegunn" rel="noopener noreferrer"&gt;
        junegunn
      &lt;/a&gt; / &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;
        fzf
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🌸 A command-line fuzzy finder
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;sup&gt;Special thanks to:&lt;/sup&gt;
&lt;br&gt;
&lt;br&gt;
&lt;a href="https://warp.dev/?utm_source=github&amp;amp;utm_medium=referral&amp;amp;utm_campaign=fzf_20240209" rel="nofollow noopener noreferrer"&gt;
  &lt;div&gt;
    &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fjunegunn%2Fi%2Fmaster%2Fwarp.png" width="300" alt="Warp"&gt;
  &lt;/div&gt;
  &lt;b&gt;Warp is a modern, Rust-based terminal with AI built in so you and your team can build great software, faster.&lt;/b&gt;
  &lt;div&gt;
    &lt;sup&gt;Visit warp.dev to learn more.&lt;/sup&gt;
  &lt;/div&gt;
&lt;/a&gt;
&lt;br&gt;

&lt;/div&gt;



&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/junegunn/i/master/fzf.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fjunegunn%2Fi%2Fmaster%2Ffzf.png" height="170" alt="fzf - a command-line fuzzy finder"&gt;&lt;/a&gt; &lt;a href="https://github.com/junegunn/fzf/actions" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/junegunn/fzf/workflows/Test%20fzf%20on%20Linux/badge.svg" alt="github-actions"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;fzf is a general-purpose command-line fuzzy finder.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/junegunn/i/master/fzf-preview.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fjunegunn%2Fi%2Fmaster%2Ffzf-preview.png" width="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's an interactive filter program for any kind of list; files, command
history, processes, hostnames, bookmarks, git commits, etc. It implements
a "fuzzy" matching algorithm, so you can quickly type in patterns with omitted
characters and still get the results you want.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Highlights&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;📦 &lt;strong&gt;Portable&lt;/strong&gt; — Distributed as a single binary for easy installation&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Blazingly fast&lt;/strong&gt; — Highly optimized code instantly processes millions of items&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Extremely versatile&lt;/strong&gt; — Fully customizable via an event-action binding mechanism&lt;/li&gt;
&lt;li&gt;🔋 &lt;strong&gt;Batteries included&lt;/strong&gt; — Includes integration with bash, zsh, fish, Vim, and Neovim&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Sponsors ❤️&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I would like to thank all the sponsors of this project who make it possible for me to continue to improve fzf.&lt;/p&gt;
&lt;p&gt;If…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Fzf&lt;/code&gt; grabs the output of any command, being it a list, processes, text... and lets you search and filter these results interactively and navigate through them. You can also assign key bindings to commands on your search.&lt;/p&gt;

&lt;p&gt;With it, we can search and navigate through any directory easily and look for files, also adding some key bindings e.g opening the file in &lt;code&gt;vscode&lt;/code&gt; when clicking enter and copying the filename path with tab.&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="nb"&gt;ls&lt;/span&gt; | fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'bat --style=numbers --color=always {}'&lt;/span&gt; &lt;span class="nt"&gt;--bind&lt;/span&gt; &lt;span class="s1"&gt;'enter:execute(code {}),tab:execute-silent(echo {} | pbcopy)+abort'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Figure 2: command for searching and navigating through a directory with key bindings using fzf




&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%2Fi%2F01g6p5q5p9l88qt1kufe.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%2Fi%2F01g6p5q5p9l88qt1kufe.png" alt="Output of the ls command using fzf"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 3: output of the ls command using fzf



&lt;p&gt;The above example uses &lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;bat&lt;/a&gt; instead of &lt;code&gt;cat&lt;/code&gt; for previewing files, as it includes some nice features like syntax highlighting and also shows modifications on files by integrating with git.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sharkdp" rel="noopener noreferrer"&gt;
        sharkdp
      &lt;/a&gt; / &lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;
        bat
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A cat(1) clone with wings.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/sharkdp/batdoc/logo-header.svg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsharkdp%2Fbatdoc%2Flogo-header.svg" alt="bat - a cat clone with wings"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b98ad4c1ad5d45c3db1f6c9c7cf661697d40ead67b2edc6f00580da600d38099/68747470733a2f2f696d672e736869656c64732e696f2f6372617465732f6c2f6261742e737667"&gt;&lt;img src="https://camo.githubusercontent.com/b98ad4c1ad5d45c3db1f6c9c7cf661697d40ead67b2edc6f00580da600d38099/68747470733a2f2f696d672e736869656c64732e696f2f6372617465732f6c2f6261742e737667" alt="license"&gt;&lt;/a&gt;
  &lt;a href="https://crates.io/crates/bat" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f1890bc7fd1e2c8b70eb84883757888d5844bd76a01d2d135ee4799338d437db/68747470733a2f2f696d672e736869656c64732e696f2f6372617465732f762f6261742e7376673f636f6c6f72423d333139653863" alt="Version info"&gt;&lt;/a&gt;&lt;br&gt;
  A &lt;i&gt;cat(1)&lt;/i&gt; clone with syntax highlighting and Git integration
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/sharkdp/bat#syntax-highlighting" rel="noopener noreferrer"&gt;Key Features&lt;/a&gt; •
  &lt;a href="https://github.com/sharkdp/bat#how-to-use" rel="noopener noreferrer"&gt;How To Use&lt;/a&gt; •
  &lt;a href="https://github.com/sharkdp/bat#installation" rel="noopener noreferrer"&gt;Installation&lt;/a&gt; •
  &lt;a href="https://github.com/sharkdp/bat#customization" rel="noopener noreferrer"&gt;Customization&lt;/a&gt; •
  &lt;a href="https://github.com/sharkdp/bat#project-goals-and-alternatives" rel="noopener noreferrer"&gt;Project goals, alternatives&lt;/a&gt;&lt;br&gt;
  [English]
  [&lt;a href="https://github.com/sharkdp/batdoc/README-zh.md" rel="noopener noreferrer"&gt;中文&lt;/a&gt;]
  [&lt;a href="https://github.com/sharkdp/batdoc/README-ja.md" rel="noopener noreferrer"&gt;日本語&lt;/a&gt;]
  [&lt;a href="https://github.com/sharkdp/batdoc/README-ko.md" rel="noopener noreferrer"&gt;한국어&lt;/a&gt;]
  [&lt;a href="https://github.com/sharkdp/batdoc/README-ru.md" rel="noopener noreferrer"&gt;Русский&lt;/a&gt;]
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Sponsors&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;A special &lt;em&gt;thank you&lt;/em&gt; goes to our biggest &lt;a href="https://github.com/sharkdp/batdoc/sponsors.md" rel="noopener noreferrer"&gt;sponsors&lt;/a&gt;:&lt;br&gt;
&lt;a href="https://workos.com/?utm_campaign=github_repo&amp;amp;utm_medium=referral&amp;amp;utm_content=bat&amp;amp;utm_source=github" rel="nofollow noopener noreferrer"&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsharkdp%2Fbatdoc%2Fsponsors%2Fworkos-logo-white-bg.svg" width="200" alt="WorkOS"&gt;
&lt;br&gt;
&lt;strong&gt;Your app, enterprise-ready.&lt;/strong&gt;
&lt;br&gt;
Start selling to enterprise customers with just a few lines of code.
&lt;br&gt;
&lt;sup&gt;Add Single Sign-On (and more) in minutes instead of months.&lt;/sup&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.warp.dev/?utm_source=github&amp;amp;utm_medium=referral&amp;amp;utm_campaign=bat_20231001" rel="nofollow noopener noreferrer"&gt;
  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsharkdp%2Fbatdoc%2Fsponsors%2Fwarp-logo.png" width="200" alt="Warp"&gt;
  &lt;br&gt;
  &lt;strong&gt;Warp is a modern, Rust-based terminal with AI built in&lt;br&gt;so you and your team can build great software, faster.&lt;/strong&gt;
  &lt;br&gt;
  Feel more productive on the command line with parameterized commands,
  &lt;br&gt;
  &lt;sup&gt;autosuggestions, and an IDE-like text editor.&lt;/sup&gt;
&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Syntax highlighting&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;bat&lt;/code&gt; supports syntax highlighting for a large number of programming and markup
languages:&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a9789c5200bdb0a22602643d7bf85f0f424ddd4259e763abc865609010c5e228/68747470733a2f2f696d6775722e636f6d2f724773646e44652e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/a9789c5200bdb0a22602643d7bf85f0f424ddd4259e763abc865609010c5e228/68747470733a2f2f696d6775722e636f6d2f724773646e44652e706e67" alt="Syntax highlighting example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Git integration&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;bat&lt;/code&gt; communicates with &lt;code&gt;git&lt;/code&gt; to show modifications with respect to the index
(see left side bar):&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/43e40bf9c20d5ceda8fa67f1d95b5c66548b2f6f8dca8403e08129991cc32966/68747470733a2f2f692e696d6775722e636f6d2f326c53573452452e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/43e40bf9c20d5ceda8fa67f1d95b5c66548b2f6f8dca8403e08129991cc32966/68747470733a2f2f692e696d6775722e636f6d2f326c53573452452e706e67" alt="Git integration example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Show non-printable characters&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;You can use the &lt;code&gt;-A&lt;/code&gt;/…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Creating the interactive branch navigator with Fzf
&lt;/h3&gt;

&lt;p&gt;With the &lt;code&gt;fzf&lt;/code&gt; command in place, we can &lt;a href="https://linuxhint.com/bash_pipe_tutorial/" rel="noopener noreferrer"&gt;pipe&lt;/a&gt; our &lt;code&gt;git branches&lt;/code&gt; command &lt;em&gt;[Fig. 1]&lt;/em&gt; to the &lt;code&gt;fzf&lt;/code&gt; part of &lt;em&gt;[Fig. 2]&lt;/em&gt; with some tweaking of the key bindings, and that will allow us to filter, copy and check out branches, just like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="nt"&gt;-each-ref&lt;/span&gt; &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'authordate:iso8601'&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'%(authordate:relative)%09%(refname:short)'&lt;/span&gt; refs/heads | fzf &lt;span class="nt"&gt;--tac&lt;/span&gt; &lt;span class="nt"&gt;--bind&lt;/span&gt; &lt;span class="s1"&gt;'enter:execute(echo {} | rev | cut -f1 | rev | xargs git checkout)+abort,tab:execute-silent(echo {} | rev | cut -f1 | rev | pbcopy)+abort'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Figure 4: command for interactively searching git branches,checking them out by pressing `enter` and copying the branch name by using `tab`





&lt;p&gt;You can then bind the command to some git alias in &lt;code&gt;~/.gitconfig&lt;/code&gt;, I have it assigned to &lt;code&gt;git brs&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%2Fi%2Fwhas4nql6bxmylrjwhzu.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%2Fi%2Fwhas4nql6bxmylrjwhzu.png" alt="git alias for the command"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 5: assigning a git alias for the command



&lt;p&gt;&lt;code&gt;Fzf&lt;/code&gt; includes numerous built-in key bindings as exiting with escape or navigating with scrolling/arrows, to name a couple. Try extending the command by adding more key bindings that fit your workflow!&lt;/p&gt;

&lt;p&gt;Here's a short example of the command in action:&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%2Fi.imgur.com%2FPn43VZz.gif" 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%2Fi.imgur.com%2FPn43VZz.gif" alt="interactive branch navigation command example"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 6: interactive branch navigation command example on the terminal



&lt;p&gt;That's it! ✨&lt;/p&gt;




&lt;p&gt;&lt;em&gt;And you, what other amazing &lt;code&gt;fzf&lt;/code&gt; commands do you use, or would you create to improve your development workflow?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>fzf</category>
      <category>productivity</category>
      <category>bash</category>
    </item>
    <item>
      <title>Dealing with Asynchrony when Writing End-To-End Tests with Puppeteer + Jest</title>
      <dc:creator>Albert Alises</dc:creator>
      <pubDate>Fri, 21 Sep 2018 09:35:01 +0000</pubDate>
      <link>https://forem.com/aalises/dealing-with-asynchrony-when-writing-end-to-end-tests-with-puppeteer--jest-n37</link>
      <guid>https://forem.com/aalises/dealing-with-asynchrony-when-writing-end-to-end-tests-with-puppeteer--jest-n37</guid>
      <description>&lt;p&gt;&lt;em&gt;In this article we present an overview on how to deal with asynchrony when performing end-to-end tests, using&lt;/em&gt; &lt;a href="https://github.com/GoogleChrome/puppeteer" rel="noopener noreferrer"&gt;&lt;strong&gt;Puppeteer&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;as a web scraper and&lt;/em&gt; &lt;a href="https://jestjs.io" rel="noopener noreferrer"&gt;&lt;strong&gt;Jest&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;as an assertion library. We will learn how to automate user action on the browser, wait for the server to return data and for our application to process and render it, to actually retrieving information from the website and comparing it to the data to see if our application actually works as expected for a given user action.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;So you got your wonderful web application up and running, and the time for testing has come.... There are many types of test, from &lt;a href="https://en.wikipedia.org/wiki/Unit_testing" rel="noopener noreferrer"&gt;&lt;strong&gt;Unit tests&lt;/strong&gt;&lt;/a&gt; where you test the individual components that compound your application, to &lt;a href="https://en.wikipedia.org/wiki/Integration_testing" rel="noopener noreferrer"&gt;&lt;strong&gt;Integration tests&lt;/strong&gt;&lt;/a&gt; where you test how these components interact with eachother. In this article we are gonna talk about yet another type of tests, the &lt;a href="https://www.techopedia.com/definition/7035/end-to-end-test" rel="noopener noreferrer"&gt;&lt;strong&gt;End-To-End (e2e) tests&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;End-to-end tests are great in order to test the whole application as a user perspective. Tha means testing that the outcome, behavior or data presented from the application is as expected for a given user interaction with it. They test from the front-end to the back-end, treating the application as a whole and simulating real case scenarios. Here it is &lt;a href="https://medium.freecodecamp.org/why-end-to-end-testing-is-important-for-your-team-cb7eb0ec1504" rel="noopener noreferrer"&gt;&lt;strong&gt;a nice article talking about what e2e tests are and their importance&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To test javascript code, one of the most common frameworks for assertions is &lt;strong&gt;Jest&lt;/strong&gt;, which allows you to perform all kinds of comparisons for your functions and code, &lt;a href="https://jestjs.io/docs/en/tutorial-react" rel="noopener noreferrer"&gt;&lt;strong&gt;and even testing React components&lt;/strong&gt;&lt;/a&gt;. In particular, to perform e2e tests, a fairly recent tool, &lt;strong&gt;Puppeteer&lt;/strong&gt;, comes to the rescue. Basically it is a &lt;a href="https://en.wikipedia.org/wiki/Web_scraping" rel="noopener noreferrer"&gt;&lt;strong&gt;web scraper&lt;/strong&gt;&lt;/a&gt; based on Chromium. According to the repo, it is a &lt;em&gt;"Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It provides some methods so you can simulate the user interaction on a browser via code, such as clicking elements, navigating through pages or typing on a keyboard. &lt;strong&gt;As having a highly trained monkey perform real case tasks on your application 🐒&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can find the github repos of both testing libraries here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jestjs" rel="noopener noreferrer"&gt;
        jestjs
      &lt;/a&gt; / &lt;a href="https://github.com/jestjs/jest" rel="noopener noreferrer"&gt;
        jest
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Delightful JavaScript Testing.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a href="https://www.npmjs.com/package/jest" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/940bb7b3014139cdf8c9174bf5dbce64fc77834f1a15660b515f0262ef43f738/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6a657374" alt="npm version"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/jestjs/jest/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6581c31c16c1b13ddc2efb92e2ad69a93ddc4a92fd871ff15d401c4c6c9155a4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667" alt="Jest is released under the MIT license."&gt;&lt;/a&gt;
  &lt;a href="https://twitter.com/intent/follow?screen_name=jestjs_" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/30c04cdab13930604ef88a88444b6492c945f0016f982ea1652e58957630bc5b/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f6a6573746a735f2e7376673f7374796c653d736f6369616c266c6162656c3d466f6c6c6f77253230406a6573746a735f" alt="Follow on Twitter"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/jestjs/jest/actions/workflows/nodejs.yml" rel="noopener noreferrer"&gt;&lt;img alt="GitHub CI Status" src="https://camo.githubusercontent.com/9df54a85db7769d50e8663fec3db9f005f590a23ea8ad183d1e61fec8c414f71/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6a6573746a732f6a6573742f6e6f64656a732e796d6c3f6c6162656c3d4349266c6f676f3d476974487562"&gt;&lt;/a&gt;
  &lt;a href="https://codecov.io/github/jestjs/jest" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Coverage Status" src="https://camo.githubusercontent.com/fa4f7f79f75c921fe61e78256582a648985898f7ad8b24cb9851514f48142115/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f6a6573746a732f6a6573742f6d61696e2e7376673f6d61784167653d3433323030"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://gitpod.io/#https://github.com/jestjs/jest" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Gitpod ready-to-code" src="https://camo.githubusercontent.com/fc7dbfa8c5b4a2d26b66b512fb6af478333f50a75e990376458de71db990b5b7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476974706f642d72656164792d2d746f2d2d636f64652d626c75653f6c6f676f3d676974706f64"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/jestjs/jestwebsite/static/img/jest-readme-headline.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fjestjs%2Fjestwebsite%2Fstatic%2Fimg%2Fjest-readme-headline.png" width="80%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🃏 Delightful JavaScript Testing&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;👩🏻‍💻 Developer Ready&lt;/strong&gt;: A comprehensive JavaScript testing solution. Works out of the box for most JavaScript projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏃🏽 Instant Feedback&lt;/strong&gt;: Fast, interactive watch mode only runs test files related to changed files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📸 Snapshot Testing&lt;/strong&gt;: Capture snapshots of large objects to simplify testing and to analyze how they change over time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See more on &lt;a href="https://jestjs.io" rel="nofollow noopener noreferrer"&gt;jestjs.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#running-from-command-line" rel="noopener noreferrer"&gt;Running from command line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jestjs/jest#additional-configuration" rel="noopener noreferrer"&gt;Additional Configuration&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#generate-a-basic-configuration-file" rel="noopener noreferrer"&gt;Generate a basic configuration file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#using-babel" rel="noopener noreferrer"&gt;Using Babel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#using-webpack" rel="noopener noreferrer"&gt;Using webpack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#using-vite" rel="noopener noreferrer"&gt;Using Vite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#using-parcel" rel="noopener noreferrer"&gt;Using Parcel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#using-typescript" rel="noopener noreferrer"&gt;Using Typescript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#documentation" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#badge" rel="noopener noreferrer"&gt;Badge&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/jestjs/jest#contributing" rel="noopener noreferrer"&gt;Contributing&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#code-of-conduct" rel="noopener noreferrer"&gt;Code of Conduct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#contributing-guide" rel="noopener noreferrer"&gt;Contributing Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#good-first-issues" rel="noopener noreferrer"&gt;Good First Issues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/jestjs/jest#credits" rel="noopener noreferrer"&gt;Credits&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#backers" rel="noopener noreferrer"&gt;Backers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#sponsors" rel="noopener noreferrer"&gt;Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#license" rel="noopener noreferrer"&gt;License&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/jestjs/jest#copyright" rel="noopener noreferrer"&gt;Copyright&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Install Jest using &lt;a href="https://yarnpkg.com/en/package/jest" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;yarn&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;yarn add --dev jest&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or &lt;a href="https://www.npmjs.com/package/jest" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install --save-dev jest&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note: Jest documentation uses &lt;code&gt;yarn&lt;/code&gt; commands, but &lt;code&gt;npm&lt;/code&gt; will also work. You can compare &lt;code&gt;yarn&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; commands in the &lt;a href="https://yarnpkg.com/en/docs/migrating-from-npm#toc-cli-commands-comparison" rel="nofollow noopener noreferrer"&gt;yarn docs, here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's get started by…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jestjs/jest" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/puppeteer" rel="noopener noreferrer"&gt;
        puppeteer
      &lt;/a&gt; / &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;
        puppeteer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JavaScript API for Chrome and Firefox
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Puppeteer&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/puppeteer/puppeteer/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/puppeteer/puppeteer/actions/workflows/ci.yml/badge.svg?branch=main" alt="build"&gt;&lt;/a&gt;
&lt;a href="https://npmjs.org/package/puppeteer" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1d876566818989a769a171c218f8dbe53d85eb0394a9d5619159cd158c2b1fd5/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f7075707065746565722e737667" alt="npm puppeteer package"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F10379601%2F29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Puppeteer is a JavaScript library which provides a high-level API to control
Chrome or Firefox over the
&lt;a href="https://chromedevtools.github.io/devtools-protocol/" rel="nofollow noopener noreferrer"&gt;DevTools Protocol&lt;/a&gt; or &lt;a href="https://pptr.dev/webdriver-bidi" rel="nofollow noopener noreferrer"&gt;WebDriver BiDi&lt;/a&gt;
Puppeteer runs in the headless (no visible UI) by default&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;
&lt;a href="https://pptr.dev/docs" rel="nofollow noopener noreferrer"&gt;Get started&lt;/a&gt; | &lt;a href="https://pptr.dev/api" rel="nofollow noopener noreferrer"&gt;API&lt;/a&gt; | &lt;a href="https://pptr.dev/faq" rel="nofollow noopener noreferrer"&gt;FAQ&lt;/a&gt; | &lt;a href="https://pptr.dev/contributing" rel="nofollow noopener noreferrer"&gt;Contributing&lt;/a&gt; | &lt;a href="https://pptr.dev/troubleshooting" rel="nofollow noopener noreferrer"&gt;Troubleshooting&lt;/a&gt;
&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm i puppeteer &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Downloads compatible Chrome during installation.&lt;/span&gt;
npm i puppeteer-core &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Alternatively, install as a library, without downloading Chrome.&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Example&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;puppeteer&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;'puppeteer'&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-c"&gt;// Or import puppeteer from 'puppeteer-core';&lt;/span&gt;
&lt;span class="pl-c"&gt;// Launch the browser and open a new blank page&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;browser&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;puppeteer&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;launch&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;browser&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;newPage&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Navigate the page to a URL.&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;goto&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'https://developer.chrome.com/'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Set screen size.&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;setViewport&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;width&lt;/span&gt;: &lt;span class="pl-c1"&gt;1080&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;height&lt;/span&gt;: &lt;span class="pl-c1"&gt;1024&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Type into search box.&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Given so, the &lt;strong&gt;Puppeteer + Jest&lt;/strong&gt; become has become a nice, open source way of testing web applications, by opening the application on the headless browser provided by puppeteer and simulating user input and/or interaction, and then checking that the data presented and the way our application reacts to the different actions is as expected with Jest.&lt;/p&gt;

&lt;p&gt;In this article we will not cover the whole workflow of testing with puppeteer + jest (such as installing, or setting the main structure of your tests, or &lt;a href="https://www.valentinog.com/blog/ui-testing-jest-puppetteer/" rel="noopener noreferrer"&gt;testing forms&lt;/a&gt;), but focus on one of the biggest things that we have to take into account when doing so: &lt;strong&gt;Asynchrony&lt;/strong&gt;. &lt;/p&gt;

&lt;h5&gt;
  
  
  🤘 &lt;a href="https://blog.logrocket.com/end-to-end-testing-react-apps-with-puppeteer-and-jest-ce2f414b4fd7" rel="noopener noreferrer"&gt;But hey, here you have a great tutorial on how to test with Puppeteer + Jest, to get you started.&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Because almost all of the web applications contain indeed some sort of &lt;strong&gt;asynchrony&lt;/strong&gt;. Data is retrieved from the back-end, and that data is then rendered on screen. However, Puppeteer performs all the operations sequentially, &lt;strong&gt;so... how can we tell him to wait until asynchronous events have happened?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3owyoYjmvDijECcQTu/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3owyoYjmvDijECcQTu/giphy.gif" alt="Can't wait"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 1: I feel you, but sometimes you have to wait just a little bit for everything to be rendered and ready, so your e2e tests do not crash and burn 



&lt;p&gt;Puppeteer offers you a way to &lt;strong&gt;wait for certain things to happen&lt;/strong&gt;, using the &lt;code&gt;waitFor&lt;/code&gt; functions available for the &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v1.8.0&amp;amp;show=api-class-page" rel="noopener noreferrer"&gt;&lt;code&gt;Page&lt;/code&gt;&lt;/a&gt; class. The changes you can track are visual changes that the user can observe. For instance you can see when something in your page has appeared, or has changed color, or has disappeared, as a result of some asynchronous call. Then one would compare these changes to be what you would expect from the interaction, and there you have it. How does it work?&lt;/p&gt;




&lt;h3&gt;
  
  
  Waiting, Waiting, Waiting...⏰
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;waitFor&lt;/code&gt; function set in Puppeteer helps us deal with asynchrony. As these functions return &lt;code&gt;Promises&lt;/code&gt;, usually the tests are performed making use of the &lt;a href="https://javascript.info/async-await" rel="noopener noreferrer"&gt;&lt;code&gt;async/await&lt;/code&gt;&lt;/a&gt; ES2017 feature. These functions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;waitForNavigation&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="nf"&gt;waitForNavigation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;networkidle2&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;Whenever a user action causes the page to navigate to another route, we sometimes have to wait a little bit before all the content is loaded. For that we have the &lt;code&gt;waitForNavigation&lt;/code&gt; function. it accepts a options object in where you can set a &lt;code&gt;timeout&lt;/code&gt; (in ms), or &lt;code&gt;waitUntil&lt;/code&gt; some condition is met. The possible values of &lt;code&gt;waitUntil&lt;/code&gt; are (according to the &lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;wonderful puppeteer documentation&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;load&lt;/strong&gt; (&lt;em&gt;default&lt;/em&gt;): consider navigation to be finished when the &lt;code&gt;load&lt;/code&gt; event is fired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;domcontentloaded&lt;/strong&gt;:  consider navigation to be finished when the &lt;code&gt;DOMContentLoaded&lt;/code&gt; event is fired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;networkidle0:&lt;/strong&gt; consider navigation to be finished when there are no more than 0 network connections for at least &lt;code&gt;500&lt;/code&gt; ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;networkidle2:&lt;/strong&gt; consider navigation to be finished when there are no more than 2 network connections for at least &lt;code&gt;500&lt;/code&gt; ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tipically, you will want to wait until the whole page is loaded (&lt;code&gt;{waitUntil: load}&lt;/code&gt;), but that does not guarantee you that everything is operative. What you can do is wait for a specific DOM element to appear that assures you that the whole page is loaded. you can do that with the following function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;waitForSelector&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This function waits for a specific &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors" rel="noopener noreferrer"&gt;CSS selector&lt;/a&gt; to appear, indicating that the element it matches to is on the DOM.&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;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#create-project-button&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;load&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#show-profileinfo-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//Triggers a navigation&lt;/span&gt;

&lt;span class="cm"&gt;/* 
We can either wait for the navigation or wait until a selector that indicates 
that the next page is operative appears
*/&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="nf"&gt;waitForNavigation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;load&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#data-main-table&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;ul&gt;
&lt;li&gt;&lt;code&gt;waitForFunction&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one waits for some function to return true in order to proceed. It is commonly used to monitor some property of a selector. It is used when the selector is not appearing on the DOM but some property of it changes (so you cannot wait for the selector because it is already there on the first place). It accepts a string or a closure as arguments.&lt;/p&gt;

&lt;p&gt;For example, you want to wait until a certain message changes. The testing would be performed by first getting the message using the &lt;code&gt;evaluate()&lt;/code&gt; Puppeteer function. Its first parameter is a function which is evaluated in the browser context (as if you were typing into the Chrome console).We then perform the asynchronous operations that change the message (clicking a button or whatever 🖱🔨), and then waiting for the message to change.&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;previousMessage&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="nf"&gt;evaluate&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="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;#message&lt;/span&gt;&lt;span class="dl"&gt;'&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="c1"&gt;//Async behaviour...&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="nf"&gt;waitForFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`document.querySelector('#message').innerHTML !== &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;previousMessage&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="c1"&gt;//Wait until the message changes&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;Using these &lt;code&gt;waitFor&lt;/code&gt; functions we can detect when something in our page changes after an async operation, now we just need to retrieve the data we want to test. &lt;/p&gt;


&lt;h3&gt;
  
  
  Retrieving data from selectors after an Async operation
&lt;/h3&gt;

&lt;p&gt;Once we have detected the changes caused by our asynchronous code, we tipically want to extract some data from our application that we can later compare to the &lt;strong&gt;expected visual result&lt;/strong&gt; from a user interaction. We do that using &lt;code&gt;evaluate()&lt;/code&gt; .The most common cases that you face when retrieving data are:&lt;/p&gt;
&lt;h4&gt;
  
  
  - Checking that a DOM element has appeared
&lt;/h4&gt;

&lt;p&gt;A pretty common case is checking that a given element has been rendered on the page, hence appearing on the DOM. For instance, after saving a post, you should find it in the saved posts section. Navigating there and querying if indeed the DOM element is there is the basic type of data that we can assert (as a &lt;strong&gt;boolean assertion&lt;/strong&gt;). &lt;/p&gt;

&lt;p&gt;Find below an example of checking if a given post with an id &lt;code&gt;post-id&lt;/code&gt;, where the id is a number we know, is present on the DOM. First we save the post, we wait for the post to be saved, go to the saved posts list/route and see that the post is there.&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;243&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`#post-card-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .button-save-post`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//The class could be added when the post is saved (making a style change on the button)&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="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`#post-card-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .post-saved`&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#goto-saved-posts-btn&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;waitForNavigation&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;post&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="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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;return&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="s2"&gt;`#post-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="kc"&gt;true&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In there, we can observe a couple of things. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The aforemenctioned need to have &lt;em&gt;unique id's&lt;/em&gt; for the tested elements. Given so, querying the selector is way easier and we do not need to do nested queries that get the element based on its  position on the DOM (&lt;em&gt;Hey, get me the first tr element from the first row of that table&lt;/em&gt; 🙋🏼).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We see how we can pass arguments to the evaluate function and use it to interpolate variables into our selectors. As the fucntion is being evaluated in another scope, you need to bind the variables from node to that new scope, and you can do that via that second parameter.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  - Checking for matching property values (e.g innerHTML, option...)
&lt;/h4&gt;

&lt;p&gt;Now imagine that instead of checking that an element is on the DOM, you actually &lt;strong&gt;want to check if the list of saved posts rendered on the page actually are the posts you have saved&lt;/strong&gt;. That is, you want to compare an &lt;strong&gt;array of strings&lt;/strong&gt; with the post names, e.g &lt;code&gt;["post1,"post2"]&lt;/code&gt;, with the saved posts of a user (which you can know beforehand for a test user, or retrieve from the server response).&lt;/p&gt;

&lt;p&gt;For doing that, you need to query all the title elements of the posts and obtain a given property from them (as it could be their innerHTML, value, id...). After that, you have to convert that information to a &lt;a href="https://www.oreilly.com/library/view/javascript-the-definitive/9781449393854/ch06s09.html" rel="noopener noreferrer"&gt;&lt;strong&gt;serializable value&lt;/strong&gt;&lt;/a&gt; (the evaluate function can only return serializable values or it will return &lt;strong&gt;null&lt;/strong&gt;, that is, always return arrays, strings or booleans, for instance, not HTMLElements...).&lt;/p&gt;

&lt;p&gt;An example performing that testing would be:&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;likedPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post1&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="s2"&gt;post2&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="s2"&gt;post3&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;list&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="nf"&gt;evaluate&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;let&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="cm"&gt;/*
        We get all the titles and get their innerHTML. We could also query 
        some property e.g title.getAttribute('value')
      */&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titles&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;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#post-title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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;innerHTML&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;out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;likedPosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Those are the most basic cases (and the ones you will be using most of the time) for testing the data of your application. &lt;/p&gt;
&lt;h4&gt;
  
  
  -Asserting that the &lt;code&gt;waitFor&lt;/code&gt; is succesful
&lt;/h4&gt;

&lt;p&gt;Another thing you can do instead of &lt;code&gt;evaluate()&lt;/code&gt; is, in case you just want to assert a boolean selector or a particular DOM change, is just assign the &lt;code&gt;waitFor()&lt;/code&gt; call to a variable and check if it is true. The downside of that method is that you will have to set an estimated &lt;strong&gt;timeout&lt;/strong&gt; to the function &lt;strong&gt;that is less than the Jest timeout set at the start&lt;/strong&gt;. ⏳&lt;/p&gt;

&lt;p&gt;If that timeout is exceeded the test will fail. It requires you to put an estimate timeout that you think is enough for the element to be rendered on your page after the request is made (&lt;em&gt;Hmm yeah, I think that around 3 seconds should be enough...&lt;/em&gt; 🤔). &lt;/p&gt;

&lt;p&gt;For example, we want to check &lt;strong&gt;if a new tag has been added to a post&lt;/strong&gt; querying the number of tag elements present before and after adding tags, that is, comparing their length and see if it has increased, denoting that the tag has indeed been added.&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;previousLength&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="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#tag&lt;/span&gt;&lt;span class="dl"&gt;'&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="c1"&gt;//Add tag async operation...&lt;/span&gt;

&lt;span class="c1"&gt;//Wait maximum 5 seconds to see if the tag has been added.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasBeenAdded&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="nf"&gt;waitForFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousLength&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;return&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;#tag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;previousLength&lt;/span&gt; 
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;previousLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasChanged&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Note that you also have to bind the variables to the &lt;code&gt;waitForFunction()&lt;/code&gt; as the third element if you specify the waitForFunction parameter as a closure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In order to get the data to compare with the information we have retrieved from the page, i.e our &lt;strong&gt;ground truth&lt;/strong&gt;, an approach is to have a controlled test user in which we know what to expect for each one of the tests (number of liked posts, written posts). In this approach we can then &lt;strong&gt;hardcode&lt;/strong&gt; 😱 the data to expect, such as the post titles, number of likes, etc... like we did on the examples of the previous section &lt;/p&gt;

&lt;p&gt;You can also &lt;strong&gt;fake the response data from the server&lt;/strong&gt;. That way you can test that the data obtained from the back-end is consistent with what is rendered into the application by responding predictable, inmutable data which you know beforehand. This serves for testing if the application responds predictably (parses correctly) the data returned from the server for a given call.&lt;/p&gt;

&lt;p&gt;On the next section we will see how to hijack the requests and provide custom data which you know. Puppeteer provides a method to achieve that, but if you want to dwelve more into faking &lt;code&gt;XMLHttpRequest&lt;/code&gt; and pretty much all the data your test manages, you should take a look into &lt;a href="https://sinonjs.org/" rel="noopener noreferrer"&gt;&lt;code&gt;Sinon.js&lt;/code&gt;&lt;/a&gt; 💖&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sinonjs" rel="noopener noreferrer"&gt;
        sinonjs
      &lt;/a&gt; / &lt;a href="https://github.com/sinonjs/sinon" rel="noopener noreferrer"&gt;
        sinon
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Test spies, stubs and mocks for JavaScript.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
    &lt;a href="https://sinonjs.org" title="Sinon.JS" rel="nofollow noopener noreferrer"&gt;
        &lt;img alt="Sinon.JS" src="https://camo.githubusercontent.com/9dd66ef181edd6760bd03542414eb3df329d4d58df90aa51e47ef57d6d7ecc01/68747470733a2f2f73696e6f6e6a732e6f72672f6173736574732f696d616765732f6c6f676f2e706e67"&gt;
    &lt;/a&gt;
    &lt;br&gt;
    Sinon.JS
&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
    Standalone and test framework agnostic JavaScript test spies, stubs and mocks (pronounced "sigh-non", named after &lt;a href="https://en.wikipedia.org/wiki/Sinon" rel="nofollow noopener noreferrer"&gt;Sinon, the warrior&lt;/a&gt;)
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://www.npmjs.com/package/sinon" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e4432f256b053f3e11bc018af9c4359ea62e98d04420a328af497a3ce8027edc/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f73696e6f6e2e7376673f7374796c653d666c6174" alt="npm version"&gt;&lt;/a&gt;
&lt;a href="https://saucelabs.com/u/sinonjs" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6c7c490e10911784c022fcd95aba3396dc2857244eebaf05120270b1d5fbabb4/68747470733a2f2f73617563656c6162732e636f6d2f6275696c647374617475732f73696e6f6e6a73" alt="Sauce Test Status"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/sinonjs/sinon" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/68a02b23b404fdd0889165c54ac6b36c0d9599eca3da02aa60b75898ec0d69a4/68747470733a2f2f636f6465636f762e696f2f67682f73696e6f6e6a732f73696e6f6e2f6272616e63682f6d61737465722f67726170682f62616467652e737667" alt="Codecov status"&gt;&lt;/a&gt;
&lt;a href="https://github.com/sinonjs/sinon#backers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/70b7a34e112f324370b0fe91480090c616e1289d923b1a4414ae37cc8a65cb51/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f73696e6f6e2f6261636b6572732f62616467652e737667" alt="OpenCollective"&gt;&lt;/a&gt;
&lt;a href="https://github.com/sinonjs/sinon#sponsors" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/131f390abf386a985f243077685dd08f85e56ec38246da5f6907b2a57f0e54e1/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f73696e6f6e2f73706f6e736f72732f62616467652e737667" alt="OpenCollective"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/sinon" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/69489b127d7f541f466aa2d28588d7238f625c2f1e00d57a5537f3aecee988b9/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646d2f73696e6f6e2e737667" alt="npm downloads per month"&gt;&lt;/a&gt;
&lt;a href="https://cdnjs.com/libraries/sinon.js" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f8f7a3847928c6066b6f92186e1a3270316ad502849e61ea7f78439274fc6b3b/68747470733a2f2f696d672e736869656c64732e696f2f63646e6a732f762f73696e6f6e2e6a732e737667" alt="CDNJS version"&gt;&lt;/a&gt;
&lt;a href="https://github.com/sinonjs/sinonCODE_OF_CONDUCT.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b939bc6b6e2370a6266a694cc4f0a583fbb99d28a82d0e5088f21739c369d3c7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6e7472696275746f72253230436f76656e616e742d76322e3025323061646f707465642d6666363962342e737667" alt="Contributor Covenant"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Compatibility&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;For details on compatibility and browser support, please see &lt;a href="https://github.com/sinonjs/sinonCOMPATIBILITY.md" rel="noopener noreferrer"&gt;&lt;code&gt;COMPATIBILITY.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;via &lt;a href="https://github.com/npm/npm" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ npm install sinon
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or via Sinon's browser builds available for download on the &lt;a href="https://sinonjs.org/releases/" rel="nofollow noopener noreferrer"&gt;homepage&lt;/a&gt;.
There are also &lt;a href="https://sinonjs.org/releases#npm-cdns" rel="nofollow noopener noreferrer"&gt;npm based CDNs&lt;/a&gt; one can use.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;See the &lt;a href="https://sinonjs.org/" rel="nofollow noopener noreferrer"&gt;sinon project homepage&lt;/a&gt; for documentation on usage.&lt;/p&gt;

&lt;p&gt;If you have questions that are not covered by the documentation, you can &lt;a href="https://stackoverflow.com/questions/tagged/sinon" rel="nofollow noopener noreferrer"&gt;check out the &lt;code&gt;sinon&lt;/code&gt; tag on Stack Overflow&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Goals&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;No global pollution&lt;/li&gt;
&lt;li&gt;Easy to use&lt;/li&gt;
&lt;li&gt;Require minimal “integration”&lt;/li&gt;
&lt;li&gt;Easy to embed seamlessly with any testing framework&lt;/li&gt;
&lt;li&gt;Easily fake any interface&lt;/li&gt;
&lt;li&gt;Ship with ready-to-use fakes for XMLHttpRequest, timers and more&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contribute?&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;See &lt;a href="https://github.com/sinonjs/sinonCONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt; for details on how you can contribute to Sinon.JS&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Backers&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Thank you to all our backers! 🙏 [&lt;a href="https://opencollective.com/sinon#backer" rel="nofollow noopener noreferrer"&gt;Become a backer&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opencollective.com/sinon#backers" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/bf6db67c98eb4bf2cd1af55bc5990d6521985303ee654428117cd3e5a02760f4/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f73696e6f6e2f6261636b6572732e7376673f77696474683d383930"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Sponsors&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Become a…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sinonjs/sinon" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;





&lt;h3&gt;
  
  
  Intercepting Requests and faking Requests with Puppeteer
&lt;/h3&gt;

&lt;p&gt;Imagine that you want to check if the list of saved posts rendered on the page is indeed correct given a list of saved posts for a certain user that we can obtain from an endpoint called &lt;code&gt;/get_saved_posts&lt;/code&gt;. To enable &lt;code&gt;requestInterception&lt;/code&gt; on puppeteer we just have to set when launching puppeteer&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;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestInterceptionEnabled&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, we can set all the request to intercept and mock the response data. Of course, that requires &lt;strong&gt;knowing the structure of the data returned by the back-end&lt;/strong&gt;. Tipically, one would store all the fake data response objects into a separate class and then, on request interception, query the endpoint called and return the corresponding data. This can be done like that, using the &lt;code&gt;page.on()&lt;/code&gt; function: &lt;/p&gt;

&lt;h5&gt;
  
  
  Disclaimer: For the API we assume the somewhat typical format &lt;code&gt;https://api.com/v1/endpoint_name&lt;/code&gt;, so the parsing to retrieve the endpoint is specific to that format for exemplification purposes, you can detect the Request made based on other parameters of course, your choice 💪
&lt;/h5&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;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_saved_posts&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;//Body has to be a string&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post1&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="s2"&gt;post2&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="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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interceptedRequest&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;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;interceptedRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nf"&gt;pop&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;responses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;]){&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;endpoint&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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;continue&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;You can see the &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v1.8.0&amp;amp;show=api-class-request" rel="noopener noreferrer"&gt;full documentation for the Request class here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One can easily see that, depending on the size of your &lt;code&gt;API&lt;/code&gt;, this can be quite complex, and also one of the charms of the e2e testing is to also test if the back-end is giving the correct information and data 😵.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All these methods require for you to enter known data, hardcoded as a response or as a variable to compare&lt;/strong&gt;. Another way to get the data to compare is to intercept the response and store the data into the variable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Getting the data to assert from the &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v1.8.0&amp;amp;show=api-class-response" rel="noopener noreferrer"&gt;&lt;code&gt;Response&lt;/code&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You can also intercept responses and get the data from there. For instance, we can intercept the &lt;code&gt;get_saved_posts&lt;/code&gt; response and store all the posts into a variable.&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;response&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/*
    Grab the response for the endpoint we want and get the data. 
    You could then switch the endpoint and retrieve different data 
    from the API, such as the post id from before, remember? 
    */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&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;url&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&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="nf"&gt;pop&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;endpoint&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_saved_posts&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;responseBody&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;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="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&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;h5&gt;
  
  
  ✍🏼 Note: The &lt;code&gt;page.on()&lt;/code&gt; methods for request and response are tipically placed on the &lt;code&gt;beforeAll()&lt;/code&gt; method of your tests, after declaring the page variable, as they define a global behavior of your test.
&lt;/h5&gt;

&lt;p&gt;So after your application has rendered everything you can query the DOM elements and then compare with the &lt;code&gt;posts&lt;/code&gt; variable to see that your app effectively renders everything as expected.&lt;/p&gt;




&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In this article we provided a comprehensive overview on how to test applications that present asynchronous data fetched from a server, by using Puppeteer + Jest.&lt;/p&gt;

&lt;p&gt;We have learned how to implement waiting for certain events to happen (e.g DOM mutations), that trigger visual changes caused by our asynchronous data. We have gone through the pipeline of detecting, querying and comparing those changes with known data so we can assess that the application works as expected from a user perspective.&lt;/p&gt;




&lt;h6&gt;
  
  
  Got any questions?! Go out here, start implementing tests like a madman and pray for them to pass, happy coding! 🙇🏻
&lt;/h6&gt;

</description>
      <category>puppeteer</category>
      <category>automation</category>
      <category>testing</category>
      <category>jest</category>
    </item>
    <item>
      <title>Preact + Typescript + Parcel + Redux Zero: Rebuilding the QMENTA Front-End focusing on Performance and Minimalism.</title>
      <dc:creator>Albert Alises</dc:creator>
      <pubDate>Mon, 17 Sep 2018 12:17:01 +0000</pubDate>
      <link>https://forem.com/aalises/preact--typescript--parcel--redux-zero-rebuilding-the-qmenta-front-end-focusing-on-performance-and-minimalism-1lnp</link>
      <guid>https://forem.com/aalises/preact--typescript--parcel--redux-zero-rebuilding-the-qmenta-front-end-focusing-on-performance-and-minimalism-1lnp</guid>
      <description>&lt;p&gt;&lt;em&gt;At&lt;/em&gt; &lt;a href="https://www.qmenta.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;QMENTA&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;we have been rebuilding the front-end from scratch, seeking simplicity and performance not only in design, but also in the technology stack used for it . This article provides a comprehensive overview of the different parts that compound the new platform, such as how decorators are extensively used or the choice of technology stack.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post was originally posted on &lt;a href="https://medium.com/@albert.alises/preact-typescript-parcel-redux-zero-rebuilding-the-qmenta-front-end-focusing-on-performance-a7c879c5b288" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://platform.qmenta.com" rel="noopener noreferrer"&gt;QMENTA platform&lt;/a&gt; has been going on for quite some time now. The old platform front-end was built using well established technologies like &lt;em&gt;JQuery, Dojo or D3&lt;/em&gt;. While being feature rich, one of the problems was the scalability and maintainability of such a big codebase and it being quite complex in terms of UX/UI. In medical imaging storing platforms, data and operations are complex enough for the user to manage, so making it more difficult in terms of user experience or visual design is a no-go zone. 🙅🏻&lt;/p&gt;

&lt;p&gt;One of the main challenges coming into this year was to rebuild the front-end from scratch to accommodate the new necessities of a growing neuroimaging processing hub, to make it clean and in a way that can be easily maintainable, scalable and up to date with the latest technologies on front-end development.&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%2Fpjwmbwn8fwj7hed4a6cc.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%2Fpjwmbwn8fwj7hed4a6cc.png" alt="Register Page" width="800" height="398"&gt;&lt;/a&gt;&lt;br&gt;Figure 1: Register Page for the new front-end
 &lt;br&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%2Fxpafdlcvu85elxuzuxvz.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%2Fxpafdlcvu85elxuzuxvz.png" alt="Project List View" width="800" height="330"&gt;&lt;/a&gt;&lt;br&gt;Figure 2: Project List view for all our projects on the new front-end
 &lt;/p&gt;

&lt;p&gt;Having speed, performance and minimalism as a flagship, the aim was to build a single page web application using the &lt;code&gt;FrameworkOfChoice&lt;/code&gt;, which can be &lt;strong&gt;VueJS, AngularJS, React&lt;/strong&gt;, or other frameworks such as &lt;a href="https://github.com/jorgebucaran/hyperapp" rel="noopener noreferrer"&gt;&lt;em&gt;Hyperapp&lt;/em&gt;&lt;/a&gt;. &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jorgebucaran" rel="noopener noreferrer"&gt;
        jorgebucaran
      &lt;/a&gt; / &lt;a href="https://github.com/jorgebucaran/hyperapp" rel="noopener noreferrer"&gt;
        hyperapp
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      1kB-ish JavaScript framework for building hypertext applications
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Hyperapp&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The tiny framework for building hypertext applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do more with less&lt;/strong&gt;—We have minimized the concepts you need to learn to get stuff done. Views, actions, effects, and subscriptions are all pretty easy to get to grips with and work together seamlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write what, not how&lt;/strong&gt;—With a declarative API that's easy to read and fun to write, Hyperapp is the best way to build purely functional, feature-rich, browser-based apps using idiomatic JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller than a favicon&lt;/strong&gt;—1 kB, give or take. Hyperapp is an ultra-lightweight Virtual DOM, &lt;a href="https://javascript.plainenglish.io/javascript-frameworks-performance-comparison-2020-cd881ac21fce" rel="nofollow noopener noreferrer"&gt;highly-optimized diff algorithm&lt;/a&gt;, and state management library obsessed with minimalism.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's the first example to get you started. &lt;a href="https://codepen.io/jorgebucaran/pen/zNxZLP?editors=1000" rel="nofollow noopener noreferrer"&gt;Try it here&lt;/a&gt;—no build step required!&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;module&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;h&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;text&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;app&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"https://unpkg.com/hyperapp"&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-v"&gt;AddTodo&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;state&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
    ...&lt;span class="pl-s1"&gt;state&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-c1"&gt;value&lt;/span&gt;: &lt;span class="pl-s"&gt;""&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-c1"&gt;todos&lt;/span&gt;: &lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jorgebucaran/hyperapp" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
Up until there, pretty standard and mainstream definition for a web application project. After pondering the different frameworks and technologies, we chose &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Preact&lt;/em&gt;&lt;/a&gt; coupled with &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;&lt;em&gt;Typescript&lt;/em&gt;&lt;/a&gt; as the core libraries of choice for the project.

&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%2F3s1bsdjgsfho7dvjxllx.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%2F3s1bsdjgsfho7dvjxllx.jpg" alt="Typescript and Preact logos" width="550" height="280"&gt;&lt;/a&gt;&lt;br&gt;Figure 3: Preact and Typescript logos.
 &lt;/p&gt;


&lt;h3&gt;
  
  
  But… why Preact + Typescript? 🤔
&lt;/h3&gt;

&lt;p&gt;Preact, according to &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;its official documentation&lt;/a&gt;, is a &lt;em&gt;“fast 3kB alternative to React with the same modern API”&lt;/em&gt;. It offers everything we like and love about React such as the &lt;a href="https://reactjs.org/docs/faq-internals.html" rel="noopener noreferrer"&gt;Virtual DOM&lt;/a&gt;, &lt;a href="https://reactjs.org/docs/react-component.html" rel="noopener noreferrer"&gt;Component-Based&lt;/a&gt; development, lifecycle hooks and &lt;a href="https://reactjs.org/docs/introducing-jsx.html" rel="noopener noreferrer"&gt;jsx syntax&lt;/a&gt; for generating dynamically, data-driven interfaces.&lt;/p&gt;

&lt;p&gt;Preact does all this while keeping the API leaner (&lt;a href="https://gist.github.com/Restuta/cda69e50a853aa64912d" rel="noopener noreferrer"&gt;React 16.2.0 + React DOM is 31.8Kb while Preact is only 4Kb, both gzipped&lt;/a&gt;), making it faster, more lightweight, and reducing a lot of the noise added by React. The principal differences with React can be seen &lt;a href="https://preactjs.com/guide/differences-to-react" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Some of them are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;this.props&lt;/code&gt; and &lt;code&gt;this.state&lt;/code&gt; are passed to &lt;code&gt;render()&lt;/code&gt; for you. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can just use class for CSS classes. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Getting rid of a lot of React / React DOM specific functions like &lt;code&gt;React.CreateElement&lt;/code&gt; or &lt;code&gt;ReactDOM.render&lt;/code&gt; , being separate exports thus avoiding the aliasing, making your code cleaner and more readable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regarding performance, testing it on a simple &lt;a href="http://todomvc.com/" rel="noopener noreferrer"&gt;TODO application&lt;/a&gt;, the results are astounding, with Preact being among the fastest of them approached by Vue.js and circa 170ms faster than React. Amazing, isn’t it? 🌠&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%2Foj9301r6d7fn53z3i2d8.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%2Foj9301r6d7fn53z3i2d8.png" alt="Performance Test" width="532" height="460"&gt;&lt;/a&gt;&lt;br&gt;Figure 4: TodoMVC Performance comparison between different frameworks. Seen at: &lt;a href="https://developit.github.io/preact-perf/" rel="noopener noreferrer"&gt;https://developit.github.io/preact-perf/&lt;/a&gt;&lt;br&gt;

 &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can perform the performance test on your device by going &lt;a href="https://developit.github.io/preact-perf/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not everything is great of course, one can find that the community is not as big or responsive as the React one (it is not that widely used after all). Some sweet functionalities are still not there, such as support for &lt;a href="https://reactjs.org/docs/fragments.html" rel="noopener noreferrer"&gt;fragments&lt;/a&gt; (So you still have to create some good old div wrappers). Furthermore, some libraries are still not supported, but worry not, &lt;a href="https://github.com/developit/preact-compat" rel="noopener noreferrer"&gt;preact-compat&lt;/a&gt; creates a layer on top of Preact to make it fully compatible with React. Magic!⚡️&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/preactjs" rel="noopener noreferrer"&gt;
        preactjs
      &lt;/a&gt; / &lt;a href="https://github.com/preactjs/preact-compat" rel="noopener noreferrer"&gt;
        preact-compat
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ATTENTION: The React compatibility layer for Preact has moved to the main preact repo.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ATTENTION: preact-compat has moved to the &lt;a href="https://github.com/preactjs/preact/tree/master/compat" rel="noopener noreferrer"&gt;main repo&lt;/a&gt;.&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;The code here is only meant for the older Preact 8.x release line. If you're still on Preact 8.x we highly recommend upgrading to the 10.x release line as it includes significant improvements and a much more stable React compatibility layer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.org/package/preact-compat" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/09ddc42b429c027c0c47dc28aa84c200f4ea1ae27255e6e5e13873207c485f12/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f7072656163742d636f6d7061742e7376673f7374796c653d666c6174" alt="NPM"&gt;&lt;/a&gt;
&lt;a href="https://travis-ci.org/developit/preact-compat" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/bae791a2b96e289e3d18487a73438d03a2e32fa97d3e0f1623bd3e9f334c5e83/68747470733a2f2f7472617669732d63692e6f72672f646576656c6f7069742f7072656163742d636f6d7061742e7376673f6272616e63683d6d6173746572" alt="travis-ci"&gt;&lt;/a&gt;
&lt;a href="https://cdnjs.com/libraries/preact-compat" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a174e9e11713c1e8304fdb77f3cad8a92d01cc51c122accd4e5c7126466454bd/68747470733a2f2f696d672e736869656c64732e696f2f63646e6a732f762f7072656163742d636f6d7061742e737667" alt="CDNJS"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Note:&lt;/strong&gt; This module is for Preact 8.x and prior - Preact X includes compat by default
For Preact X, please uninstall &lt;code&gt;preact-compat&lt;/code&gt; and replace your aliases with &lt;code&gt;preact/compat&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This module is a compatibility layer that makes React-based modules work with &lt;a href="https://github.com/developit/preact" rel="noopener noreferrer"&gt;Preact&lt;/a&gt;, without any code changes.&lt;/p&gt;
&lt;p&gt;It provides the same exports as &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt;, meaning you can use your build tool of choice to drop it in where React is being depended on.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Interested? Here's an example project that uses &lt;code&gt;preact-compat&lt;/code&gt; to work with an existing React library unmodified
achieving more than 95% reduction in size:&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/preactjs/preact-compat" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&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%2Fas6h7tr8jarm0priepum.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%2Fas6h7tr8jarm0priepum.png" alt="Analysis List View" width="800" height="399"&gt;&lt;/a&gt;&lt;br&gt;Figure 5: Analysis List View
 &lt;/p&gt;

&lt;p&gt;We ❤️ &lt;strong&gt;Typescript&lt;/strong&gt;. A lot. For an application that is rich in data and manages a lot of different metadata and results from APIs, Typescript offers us static type checking that comes very handy when defining the different data structures that our application handles, as well as the structure of the props and state of the different components, so we know what to expect, have it documented and have all the data to be consistent at all different stages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You can create interfaces with Typescript that characterize the input/output data your application manages. In our case, it helps modeling the data necessary to characterize a subject, or an analysis so everything is consistant and we know what to expect when dealing with those structures.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AnalysisInput&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;container_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;in_out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[][],&lt;/span&gt;
  &lt;span class="nx"&gt;passed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;subject_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AnalysisSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;acpc_alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;age_months&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;alignment_tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnalysisInput&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With Typescript, you can also create &lt;em&gt;Custom Components&lt;/em&gt; like the one below. This enforces the classes extending ComponentForm to implement methods for handling the form change and also setting the form model (i.e the object with all the fields required for the form such as the user, password, etc…), this model is then required as the state of the component, also a method &lt;code&gt;submitForm()&lt;/code&gt; has to be implemented. With that, we have a skeleton or structure that all forms follow with a generic form component that can have any given number of fields.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&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="s2"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FormManagement&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;M&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ComponentForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormManagement&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;setFormModel &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&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="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;handleFormChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&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="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&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="na"&gt;form&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;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;submitForm&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;An example of a simplified generic Preact Component our application uses that enforce Typescript. We can see the different lifecycle hooks and how the props and state of the component are set as Typescript interfaces.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;mport&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&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="s2"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CommonSelectorAttrs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;class&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;printBy&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="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;any&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;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CommonSelectorState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;innerValue&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CommonSelector&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CommonSelectorAttrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CommonSelectorState&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;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;visible&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;innerValue&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="nx"&gt;getValue&lt;/span&gt; &lt;span class="o"&gt;=&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;String&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;printBy&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;printBy&lt;/span&gt;&lt;span class="p"&gt;]&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="nf"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&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="na"&gt;innerValue&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="nf"&gt;getValue&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;selectOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opt&lt;/span&gt; &lt;span class="p"&gt;}});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;closeTooltip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="nf"&gt;setState&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="na"&gt;visible&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkMatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getMatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;checkMatch&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;match&lt;/span&gt; &lt;span class="o"&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;opt&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;match&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="nf"&gt;selectOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&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="nf"&gt;setState&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="na"&gt;innerValue&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;explicitSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opt&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="nf"&gt;setState&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="na"&gt;innerValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&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="nf"&gt;closeTooltip&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setValue&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerValue&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;return&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="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerValue&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;render&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="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Select one option"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&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;opt&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;explicitSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;h3&gt;
  
  
  Decorators, Decorators… 💎
&lt;/h3&gt;

&lt;p&gt;Ah, &lt;a href="https://www.sitepoint.com/javascript-decorators-what-they-are/" rel="noopener noreferrer"&gt;Javascript decorators&lt;/a&gt;. As a concept, they simply act as wrappers to another function, extending its functionality, like &lt;a href="https://reactjs.org/docs/higher-order-components.html" rel="noopener noreferrer"&gt;&lt;strong&gt;higher-order functions&lt;/strong&gt;&lt;/a&gt;. We extensively use decorators to keep our code cleaner and separate the back-end, state or application management concerns and provide a more structured way to define common functions across components like connecting to the Redux store, connecting to the API or defining some asynchronous behavior to happen before or after the responses are sent (sort of like method proxies), so we do not have to repeat code and provide an elegant, minimal way to define these behaviors. We use them for:&lt;/p&gt;
&lt;h5&gt;
  
  
  - Asynchronous Method Interceptors
&lt;/h5&gt;

&lt;p&gt;For managing asynchronous behavior we use &lt;a href="https://github.com/k1r0s/kaop-ts" rel="noopener noreferrer"&gt;&lt;code&gt;kaop-TS&lt;/code&gt;&lt;/a&gt; , which is a decorator library that provides some method interceptors written in Typescript. With them, we can plug in behavior at a join point on the asynchronous method, like perform some operations before the method starts, or after the method has finished, or plugging in a method that intercepts an error and performs some logic. We can see the &lt;code&gt;beforeMethod&lt;/code&gt; decorator being used in the snippet below where we define the http decorator.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/k1r0s" rel="noopener noreferrer"&gt;
        k1r0s
      &lt;/a&gt; / &lt;a href="https://github.com/k1r0s/kaop-ts" rel="noopener noreferrer"&gt;
        kaop-ts
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Simple Yet Powerful Library of ES2016 Decorators with Strongly typed method Interceptors like BeforeMethod, AfterMethod, OnException, etc
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/904566a4a4ad4e0a4874e423e11cacf35481fdbf8d7e225f07b8e75fd64fa091/687474703a2f2f692e696d6775722e636f6d2f366269457073712e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/904566a4a4ad4e0a4874e423e11cacf35481fdbf8d7e225f07b8e75fd64fa091/687474703a2f2f692e696d6775722e636f6d2f366269457073712e706e67" alt="kaop"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/semantic-release/semantic-release" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/251b82ec02847188c7f2f024d0a6752bb8e0422772baaace42e7a7dc3fd8c88a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2532302532302546302539462539332541362546302539462539412538302d73656d616e7469632d2d72656c656173652d6531303037392e737667" alt="semantic-release"&gt;&lt;/a&gt;
&lt;a href="https://greenkeeper.io/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/37884e26204ff1033b4023ad2422f888283cbde671f603167ca8443eec6ee813/68747470733a2f2f6261646765732e677265656e6b65657065722e696f2f6b317230732f6b616f702d74732e737667" alt="Greenkeeper badge"&gt;&lt;/a&gt;
&lt;a href="https://travis-ci.org/k1r0s/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/16b60362d7fd4bf657a51f016f9c64cd0e3b96a8c7b647191843e28b4720bfa5/68747470733a2f2f7472617669732d63692e6f72672f6b317230732f6b616f702d74732e7376673f6272616e63683d6d6173746572" alt="Image travis"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/kaop-ts/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/11599b137b2c26c920a3ce2051bdeba6b38d26642657f748feb2548e0a63d5cd/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6b616f702d74732e737667" alt="version"&gt;&lt;/a&gt;
&lt;a href="https://coveralls.io/github/k1r0s/kaop-ts?branch=master" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/31a622e6d50ce33f7308ec9dc67b777f16bcaf00ed1a96df37673e128283edcb/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6b317230732f6b616f702d74732f62616467652e7376673f6272616e63683d6d6173746572" alt="Coverage Status"&gt;&lt;/a&gt;
&lt;a href="https://david-dm.org/k1r0s/kaop-ts/status.svg" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5e8da2e3040d679995fe88e4bf413fe46a65f309ed0066a096d756d979ec5e6d/68747470733a2f2f64617669642d646d2e6f72672f6b317230732f6b616f702d74732f7374617475732e737667" alt="dependencies"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/kaop-ts" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ccfb4f9ba4ebf33850fedc456d93bc68ea98412a708aa3fa3caccdb8638b7c15/68747470733a2f2f64617669642d646d2e6f72672f6b317230732f6b616f702d74732f6465762d7374617475732e737667" alt="dev-dependencies"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/kaop-ts" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8f3007a65306f52ab4a8284341a3f87c29e2c92e2e4b13751158a28e0011eaee/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646d2f6b616f702d74732e737667" alt="downloads"&gt;&lt;/a&gt;
&lt;a href="https://snyk.io/test/npm/kaop-ts" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1c70130a570580c9cf0ebed2dcf40e86f5cf42ebe1ceb64481f6ac56cc1bfdef/68747470733a2f2f736e796b2e696f2f746573742f6e706d2f6b616f702d74732f62616467652e737667" alt="Known Vulnerabilities"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lightweight, solid, framework agnostic and &lt;strong&gt;easy to use&lt;/strong&gt; library written in TypeScript to deal with &lt;em&gt;Cross Cutting Concerns&lt;/em&gt; and improve modularity in your code.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Short Description (or what is an Advice)&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;This library provides a straightforward manner to implement &lt;strong&gt;Advices&lt;/strong&gt; in your app. Advices are pieces of code that can be plugged in several places within OOP paradigm like 'beforeMethod', 'afterInstance'.. etc. Advices are used to change, extend, modify the behavior of methods and constructors non-invasively.&lt;/p&gt;

&lt;p&gt;For in deep information about this technique check the &lt;a href="https://github.com/k1r0s/kaop-ts#resources" rel="noopener noreferrer"&gt;resources&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Demo&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://jsbin.com/bogecojuvi/edit?js,console" rel="nofollow noopener noreferrer"&gt;https://jsbin.com/bogecojuvi/edit?js,console&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Get started&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install kaop-ts --save&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Use a &lt;a href="https://github.com/k1r0s/kaop-ts/docs/api.md#available-join-points" rel="noopener noreferrer"&gt;join point&lt;/a&gt; to plug it to any method/class:&lt;/p&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;afterMethod&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;'kaop-ts'&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;double&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;afterMethod&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;meta&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-s1"&gt;meta&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;result&lt;/span&gt; &lt;span class="pl-c1"&gt;*=&lt;/span&gt; &lt;span class="pl-c1"&gt;2&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;

&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;DummyExample&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;

  @&lt;span class="pl-s1"&gt;double&lt;/span&gt;
  &lt;span class="pl-k"&gt;static&lt;/span&gt; &lt;span class="pl-en"&gt;calculateSomething&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;num&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;num2&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;num&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;num2&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;

&lt;span class="pl-v"&gt;DummyExample&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;calculateSomething&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;3&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;3&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/k1r0s/kaop-ts" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h5&gt;
  
  
  - Connecting with and Managing API calls
&lt;/h5&gt;

&lt;p&gt;For managing the calls to the QMENTA API, we implemented a &lt;code&gt;@platformRequest(url,method)&lt;/code&gt; decorator that you can wrap in any function of with signature &lt;code&gt;function(params,error?,result?)&lt;/code&gt; and convert it into an asyncronous call to the API with certain parameters to send in the request and receive the result JSON object from the call or the error thrown by the request. The implementation of the decorator can be seen below as well as an example method calling it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;meta&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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get&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="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;aditional&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&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;params&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;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;aditional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;aditional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;err&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;checkInvalidCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;err&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wrong response format! (not a json obj)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stringifyParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&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="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;platformRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;noop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&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="nf"&gt;beforeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;noop&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stringifyParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;checkError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="cm"&gt;/* USAGE EXAMPLE */&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ChangePasswordRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;old_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;new_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;new_password_confirm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;platformRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/change_password&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="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;RecoverPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangePasswordRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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="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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;//Cannot change the password&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="c1"&gt;//Password changed succesfully!&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;blockquote&gt;
&lt;p&gt;&lt;em&gt;The decorator uses &lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;axios&lt;/a&gt; under the hood to perform the request&lt;/em&gt; 🕵. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An implementation of it (as a minimal library) wrapping the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;Fetch API&lt;/a&gt; can be seen on the &lt;a href="https://github.com/aalises/http-fetch-decorator" rel="noopener noreferrer"&gt;&lt;code&gt;http-fetch-decorator&lt;/code&gt;&lt;/a&gt; repo:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/aalises" rel="noopener noreferrer"&gt;
        aalises
      &lt;/a&gt; / &lt;a href="https://github.com/aalises/http-fetch-decorator" rel="noopener noreferrer"&gt;
        http-fetch-decorator
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Simple Fetch interface http decorator wrapper for your functions.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;HTTP Fetch Decorator&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Simple Fetch interface decorator wrapper for your functions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install http-fetch-decorator&lt;/code&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Signature&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;Then you can place the decorator which has the same input structure as the fetch function. The parameters of the request or the data to send as a body are passed via arguments to the wrapped function, and the result and error objects are available as parameters of the function as well:&lt;/p&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;@&lt;span class="pl-v"&gt;Fetch&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-v"&gt;Url&lt;/span&gt;: &lt;span class="pl-v"&gt;String&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;initObj&lt;/span&gt;?: &lt;span class="pl-v"&gt;Object&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-k"&gt;static&lt;/span&gt; &lt;span class="pl-en"&gt;wrappedFunction&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;params&lt;/span&gt;: &lt;span class="pl-v"&gt;Object&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;result&lt;/span&gt;? : &lt;span class="pl-v"&gt;Response&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;err&lt;/span&gt;? : &lt;span class="pl-s1"&gt;any&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  ...
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;How to use it&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-v"&gt;Fetch&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"http-fetch-decorator"&lt;/span&gt;
&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;AnyES6Class&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  @&lt;span class="pl-v"&gt;Fetch&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"apiexample/getsomething"&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;method&lt;/span&gt;: &lt;span class="pl-s"&gt;'GET'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;&lt;span class="pl-c1"&gt;mode&lt;/span&gt;:&lt;span class="pl-s"&gt;'cors'&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
  &lt;span class="pl-k"&gt;static&lt;/span&gt; &lt;span class="pl-en"&gt;parseResponse&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;id&lt;/span&gt;: &lt;span class="pl-s"&gt;'1'&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;&lt;span class="pl-s1"&gt;result&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;&lt;span class="pl-s1"&gt;err&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;if&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;err&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-k"&gt;throw&lt;/span&gt; &lt;span class="pl-s1"&gt;err&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-c"&gt;//Result contains the&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/aalises/http-fetch-decorator" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Also, the state management and routing of our application use decorators to extend functionality to the components to, for example, connect it to the store or listen to be rendered at a specific route. On the next section we will talk a little bit more about it.&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%2F8emsdtlancgys32461hx.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%2F8emsdtlancgys32461hx.png" alt="Subject Detail Viewing" width="800" height="398"&gt;&lt;/a&gt;&lt;br&gt;Figure 6: Specific Subject files viewing with the edit file metadata modal.&lt;br&gt;

 &lt;/p&gt;


&lt;h3&gt;
  
  
  State Management and Routing 🚀
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;State management&lt;/strong&gt; is one of the main concerns of any growing React application. When the number of entities and components keep growing, the need for global state management arises. &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; is one of the mainstream solutions that aims to solve that. This post assumes some previous knowledge of how Redux operates; if not, &lt;a href="https://medium.freecodecamp.org/understanding-redux-the-worlds-easiest-guide-to-beginning-redux-c695f45546f6" rel="noopener noreferrer"&gt;here you can find a guide on how it works&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;In our application, we did not want to have a big store and we tried to keep it as small as possible, reducing the use of the shared state, enforcing encapsulation and separation of concerns. Given so, we decided to use a lightweight (less than 1Kb) version called &lt;a href="https://github.com/redux-zero/redux-zero" rel="noopener noreferrer"&gt;&lt;code&gt;Redux-Zero&lt;/code&gt;&lt;/a&gt;. While having some differences, it also reduces a lot of the unnecessary overhead (for the purpose of our application) that Redux has. It still has a &lt;strong&gt;store&lt;/strong&gt; (albeit just one), which provides all the global state that we want to manage; in our case, session and project information, current pages, notifications of the current project id, among others. It also has &lt;strong&gt;no reducers&lt;/strong&gt;, which ironically does reduce the complexity of the library quite a lot.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/redux-zero" rel="noopener noreferrer"&gt;
        redux-zero
      &lt;/a&gt; / &lt;a href="https://github.com/redux-zero/redux-zero" rel="noopener noreferrer"&gt;
        redux-zero
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A lightweight state container based on Redux 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6129dbe5ac35996ad4652c7bf93c600082334b255b2d5250de5c42953934c503/68747470733a2f2f692e696d6775722e636f6d2f53386a6e72384f2e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/6129dbe5ac35996ad4652c7bf93c600082334b255b2d5250de5c42953934c503/68747470733a2f2f692e696d6775722e636f6d2f53386a6e72384f2e706e67" height="300px" alt="redux zero logo" title="redux zero logo"&gt;&lt;/a&gt;
  &lt;br&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A lightweight state container based on Redux&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Read &lt;a href="https://medium.com/@matheusml/introducing-redux-zero-bea42214c7ee" rel="nofollow noopener noreferrer"&gt;the intro blog post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.codacy.com/app/matheusml/redux-zero?utm_source=github.com&amp;amp;utm_medium=referral&amp;amp;utm_content=redux-zero/redux-zero&amp;amp;utm_campaign=Badge_Grade" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/33cd0f91c7dc915760cd52ede4d26e59eefc1913cfba887ebb96d09efc523739/68747470733a2f2f6170692e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f6134616466313331353662643434343161653133326432643964633732313836" alt="codacy"&gt;&lt;/a&gt;
&lt;a href="https://travis-ci.org/redux-zero/redux-zero" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/15b3006513c6e9f7bfade24ad57787a25247d1f4fea26ca22b9de9fb3ff24195/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f72656475782d7a65726f2f72656475782d7a65726f2f6d61737465722e737667" alt="build"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/redux-zero" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a48d239c8b8892169d67f0949b7dc0121a35568c037a2f9e29bfc0a7a504fc3c/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f72656475782d7a65726f2e737667" alt="npm"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/redux-zero" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/564b34f6b9e2ddb4c1ae9991f501b6f5527220622d50c03310f85d1a81d5e146/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646d2f72656475782d7a65726f2e737667" alt="downloads"&gt;&lt;/a&gt;
&lt;a href="https://github.com/redux-zero/redux-zero" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/27ca0eb93b80cda8cfd72e24b4d0439e694eb1dee7355cb1e4e733e2f52a4b5c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72656475782d7a65726f2f72656475782d7a65726f2e737667" alt="license"&gt;&lt;/a&gt;
&lt;a href="https://github.com/redux-zero/redux-zero" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9311010816b38b04245a24f6f520f39da7d802a1995f0e039114bc05c17074a8/68747470733a2f2f696d672e736869656c64732e696f2f64617669642f72656475782d7a65726f2f72656475782d7a65726f2e737667" alt="dependencies"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#installation" rel="noopener noreferrer"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#how" rel="noopener noreferrer"&gt;How&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#example" rel="noopener noreferrer"&gt;Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#actions" rel="noopener noreferrer"&gt;Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#async" rel="noopener noreferrer"&gt;Async&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#middleware" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#devtools" rel="noopener noreferrer"&gt;DevTools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#typescript" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#inspiration" rel="noopener noreferrer"&gt;Inspiration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#roadmap" rel="noopener noreferrer"&gt;Roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redux-zero/redux-zero#docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;To install the stable version:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;npm i redux-zero
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This assumes that you’re using &lt;a href="https://www.npmjs.com/" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt; with a module bundler like &lt;a href="https://webpack.js.org/" rel="nofollow noopener noreferrer"&gt;webpack&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;ES2015+:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;createStore&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"redux-zero"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-v"&gt;Provider&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;connect&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"redux-zero/react"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;TypeScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;createStore&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"redux-zero"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-v"&gt;Provider&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;connect&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"redux-zero/react"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;CommonJS:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;createStore&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;require&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"redux-zero"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; Provider&lt;span class="pl-kos"&gt;,&lt;/span&gt; connect &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;require&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"redux-zero/react"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;UMD:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&amp;lt;!-- the store --&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://unpkg.com/redux-zero/dist/redux-zero.min.js&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-c"&gt;&amp;lt;!-- for react --&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://unpkg.com/redux-zero/react/index.min.js&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="pl-c"&gt;&amp;lt;!-- for preact --&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://unpkg.com/redux-zero/preact/index.min.js&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="pl-c"&gt;&amp;lt;!-- for vue --&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/redux-zero/redux-zero" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&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%2F967ye2l9i8cir7wnkpyw.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%2F967ye2l9i8cir7wnkpyw.png" alt="Brain Fibers Visualizer" width="800" height="398"&gt;&lt;/a&gt;&lt;br&gt;Figure 7: Brain Tractography Fiber visualization on the front-end using WebGL&lt;br&gt;

 &lt;/p&gt;

&lt;p&gt;To get it up and working, just wrap your root component with the &lt;code&gt;&amp;lt;Provider store={store}/&amp;gt;&lt;/code&gt; component and set the store on it, an object created by the &lt;code&gt;createStore(state)&lt;/code&gt; method, where the state is the object that contains our shared state. To change that state, you create actions which are just pure functions that update this global state , e.g &lt;code&gt;setSession = (state,val) =&amp;gt; ({sessions: val});&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To call those actions from a component, we have to &lt;strong&gt;connect&lt;/strong&gt; that component to the store. Connecting a given component to the store allows the component to gain access to some actions and the global state via props. We created a decorator to simplify the process of connecting a component to the store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&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="s2"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;Connect&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="s2"&gt;redux-zero/preact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}):&lt;/span&gt; &lt;span class="nx"&gt;any&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;Child&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Connect&lt;/span&gt; &lt;span class="na"&gt;mapToProps&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;state&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mappedProps&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;mappedProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;&amp;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;With this decorator, we can plug it in on top of any component specifying the actions the component will get as props. e.g by putting &lt;code&gt;@connectStore({removeSession, setCurrentPages});&lt;/code&gt; right on top of the declaration of the component, you have access to these actions inside the component which update the global state on the store, while also having access to the global state itself via props ( &lt;code&gt;this.props.removeSession();&lt;/code&gt; ). With this method, we provide a cleaner, more elegant and compact way to connect a component to the store.&lt;/p&gt;

&lt;p&gt;Another integral part of any modern application is the option to route between the different views of the project depending on the URL or other parameters, being able to pass parameters to the routes, etc. A common solution is to use &lt;a href="https://github.com/ReactTraining/react-router" rel="noopener noreferrer"&gt;the amazing router that comes with React&lt;/a&gt; . As much as we like it, it comes with a lot of functionality that we would not really be using, so we made our own &lt;a href="https://github.com/k1r0s/preact-routlet" rel="noopener noreferrer"&gt;preact-routlet&lt;/a&gt; , a simple router for Preact/React based in decorators.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/k1r0s" rel="noopener noreferrer"&gt;
        k1r0s
      &lt;/a&gt; / &lt;a href="https://github.com/k1r0s/preact-routlet" rel="noopener noreferrer"&gt;
        preact-routlet
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Simple `Component Driven` Routing for Preact/React using ES7 Decorators
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Preact Routlet&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This package contains routing functionalities for &lt;a href="https://github.com/developit/preact" rel="noopener noreferrer"&gt;Preact&lt;/a&gt; and, now from 1.0.0 &lt;code&gt;React&lt;/code&gt; applications as well. Instead of using HTML5 history API it uses the oldie &lt;code&gt;/#/what-ever&lt;/code&gt; hash routing (this will change in the future).&lt;/p&gt;

&lt;p&gt;This project was created by exploring contextual ways to define routes rather than placing all the routes in a single file.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Usage:&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;Available imports:&lt;/p&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;// if you're using React&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;renderOnRoute&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;navigate&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;RouterOutlet&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;PathLookup&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;routePool&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;Link&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"preact-routlet/react"&lt;/span&gt;
&lt;span class="pl-c"&gt;// if you're using Preact&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;renderOnRoute&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;navigate&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;RouterOutlet&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;PathLookup&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;routePool&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;Link&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"preact-routlet/preact"&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Either &lt;code&gt;from "preact-routlet/preact"&lt;/code&gt; or &lt;code&gt;from "preact-routlet/react"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Place your &lt;code&gt;RouterOutlet&lt;/code&gt; element somewhere in your JSX:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;RouterOutlet&lt;/span&gt; &lt;span class="pl-kos"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Plug the &lt;code&gt;renderOnRoute&lt;/code&gt; decorator on some component&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;@&lt;span class="pl-en"&gt;renderOnRoute&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"/login"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; &lt;span class="pl-k"&gt;default&lt;/span&gt; &lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;Login&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-v"&gt;Component&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s1"&gt;any&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;any&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/k1r0s/preact-routlet" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The intricacies of it are simple, just wrap your application with the router component &lt;code&gt;&amp;lt;RouterOutlet /&amp;gt;&lt;/code&gt; and you are ready to go. You can also wrap some components with &lt;code&gt;&amp;lt;PathLookup shouldRender={condition}/&amp;gt;&lt;/code&gt; specifying a condition to render some path, or use the &lt;code&gt;&amp;lt;RouterOutlet redirect="/route" shouldRedirect={condition} /&amp;gt;&lt;/code&gt; to specify a condition which, in case it is met, the router automatically redirects to the specified route (for example, we use it to redirect to the login if the session is invalid or has expired).&lt;/p&gt;

&lt;p&gt;To navigate to a route you just have to call &lt;code&gt;navigate("/route")&lt;/code&gt; and to specify a Component to be rendered at a specific route, you just have to plug the decorator on top of the component e.g &lt;code&gt;@renderOnRoute("forgot-password")&lt;/code&gt; making it clear and visual in which route the component is rendered.&lt;/p&gt;

&lt;p&gt;With that, we have a compact way of representing routing and state management with the signatures on top of the component that makes it very readable. A dummy component that connects to the store and is rendered on a given route can be seen below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&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="s2"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;renderOnRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;navigate&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="s2"&gt;preact-routlet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;setLoginMessage&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="s2"&gt;../actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;connectStore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../../connect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;platformRequest&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="s2"&gt;../../../services/api-decorators&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//With these decorators, we know the route this component is rendered at and which actions does it have access to, in a compact way&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;renderOnRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/forgot-password&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="nd"&gt;connectStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;setLoginMessage&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ForgotPassword&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;any&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;//Call the forgot_password API with a POST&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;platformRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/forgot_password&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="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;performLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//Params are the parameters of the request e.g {email: "test@gmail.com , user_id: 1234}&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;err&lt;/span&gt; &lt;span class="o"&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;error&lt;/span&gt;&lt;span class="p"&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="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There was an error with the request&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="k"&gt;else&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLoginMessage&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;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//Calling the store to set a login warning message&lt;/span&gt;
      &lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//Navigate back to the login using the router&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nf"&gt;render&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="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Just an example component with some decorators...
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;h3&gt;
  
  
  Bundling with Parcel 📦
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;, not to be confused with the australian band &lt;a href="https://www.youtube.com/watch?v=PTFK96KNfIE" rel="noopener noreferrer"&gt;Parcels&lt;/a&gt;, defines itself as a &lt;em&gt;“blazing fast, zero configuration web application bundler”&lt;/em&gt;. At the start of the project, we used the fairly standard Webpack for bundling our application. It required quite a lot of plugins and configuration. (&lt;code&gt;webpack.config.ts&lt;/code&gt;). Switching to Parcel, we don’t need configuration for typescript or html files anymore, just adding &lt;code&gt;npm install --save-dev parcel-bundler parcel-plugin-typescript&lt;/code&gt; does the trick.&lt;/p&gt;

&lt;p&gt;The only remaining thing is to specify an entry point and output directory and voilà, it just works. The difference in speed and performance compared to webpack is not very acute in our case (it’s essentially the same in terms of speed), but it’s the zero configuration and minimality of Parcel what makes it our bundler of choice for the project.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/parcel-bundler" rel="noopener noreferrer"&gt;
        parcel-bundler
      &lt;/a&gt; / &lt;a href="https://github.com/parcel-bundler/parcel" rel="noopener noreferrer"&gt;
        parcel
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The zero configuration build tool for the web. 📦🚀
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a href="https://parceljs.org/" rel="nofollow noopener noreferrer"&gt;
    &lt;img alt="Parcel" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F19409%2F135924939-03845d0b-e7bb-414b-89b6-e627dfa9f614.png" width="749"&gt;
  &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/parcel-bundler/parcel#backers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b25e7230f2b59a8a8f18c8a41865c9b697c199dbf7a91beca935ac2753e77efc/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f70617263656c2f6261636b6572732f62616467652e737667" alt="Backers on Open Collective"&gt;&lt;/a&gt; &lt;a href="https://github.com/parcel-bundler/parcel#sponsors" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/148f08e943ee9d56851fa0bf3ce393ed020af1ae0cc8d64ebc9afe99f162e9cc/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f70617263656c2f73706f6e736f72732f62616467652e737667" alt="Sponsors on Open Collective"&gt;&lt;/a&gt;
&lt;a href="https://dev.azure.com/devongovett/devongovett/_build/latest?definitionId=1" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/62ab922e1610a39e65a0856e172b6fa8c68ccea81afa9e9a2f2217afd68ebc2f/68747470733a2f2f6465762e617a7572652e636f6d2f6465766f6e676f766574742f6465766f6e676f766574742f5f617069732f6275696c642f7374617475732f70617263656c2d62756e646c65722e70617263656c3f6272616e63684e616d653d7632" alt="Build Status"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/parcel" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6bf008eb65e506509bababd5c7c0df47a9d8829a43724491c8529ee33f72d83e/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f70617263656c2e737667" alt="npm package"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/parcel" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/bab8e187fc60ffc82b8166cf2a21c889ba02fa315c901fafaa721333108d0593/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646d2f70617263656c2e737667" alt="npm package"&gt;&lt;/a&gt;
&lt;a href="https://discord.gg/XSCzqGRuvr" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6312ac4d881bec870addec974491638f0d45a20be059c0e27aa9379eec3b31f6/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f383934323838333336303935363930373533" alt="Discord"&gt;&lt;/a&gt;
&lt;a href="https://twitter.com/parceljs" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/cc61262ef9e314036eeab445e04bed2548264fb691b1b295cdf9bab34605aa0c/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f70617263656c6a732e7376673f7374796c653d736f6369616c" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Parcel is a zero configuration build tool for the web. It combines a great out-of-the-box development experience with a scalable architecture that can take your project from just getting started to massive production application.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;😍 &lt;strong&gt;Zero config&lt;/strong&gt; – Parcel supports many languages and file types out of the box, from web technologies like HTML, CSS, and JavaScript, to assets like images, fonts, videos, and more. It has a built-in dev server with hot reloading, beautiful error diagnostics, and much more. No configuration needed!&lt;/li&gt;
&lt;li&gt;⚡️ &lt;strong&gt;Lightning fast&lt;/strong&gt; – Parcel's JavaScript compiler is written in Rust for native performance. Your code is built in parallel using worker threads, utilizing all of the cores on your machine. Everything is cached, so you never build the same code twice. It's like using watch mode, but even when you restart Parcel!&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Automatic production optimization&lt;/strong&gt; – Parcel optimizes your whole app for production automatically…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/parcel-bundler/parcel" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&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%2F4xzvy64v1h3eynb409r4.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%2F4xzvy64v1h3eynb409r4.png" alt="Parcel Performance" width="532" height="85"&gt;&lt;/a&gt;&lt;br&gt;Figure 8: Screenshot of the build of the whole application. Parcel’s magic💥
 &lt;/p&gt;

&lt;p&gt;The only downside is that, in order to get the &lt;a href="https://parceljs.org/hmr.html" rel="noopener noreferrer"&gt;hot reloading&lt;/a&gt; working for the dev server in Preact + Typescript, you have to add the &lt;code&gt;module.hot.accept()&lt;/code&gt; flag to your root component and specify some types in the render function (the third argument as &lt;code&gt;foo.lastChild as Element&lt;/code&gt; for Typescript not to complain. The fix can be seen on the following snippet.&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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&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="s2"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Main&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mountNode&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;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;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Main&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;mountNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mountNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastChild&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Hot Module Replacement&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&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;h3&gt;
  
  
  Unit and e2e acceptance testing with Jest + Puppeteer
&lt;/h3&gt;

&lt;p&gt;Testing is an integral part of any project. In our project, we use the fairly standard &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; for &lt;a href="https://jestjs.io/docs/en/tutorial-react" rel="noopener noreferrer"&gt;testing our components&lt;/a&gt; coupled with &lt;a href="https://github.com/GoogleChrome/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; , wich is a web scraper, or having your own highly trained monkey perform the operations you tell him/her on the browser, like clicking a certain button or dragging the mouse over an element. Using those, we can perform some operations on the front-end via a headless Chrome API and then check for expected results with Jest, like checking an element of confirmation appears or the warning message displayed is correct👌. If you want to learn more on how to use Jest + Puppeteer for testing in React, &lt;a href="https://blog.bitsrc.io/testing-your-react-app-with-puppeteer-and-jest-c72b3dfcde59" rel="noopener noreferrer"&gt;there is a nice article talking about it&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/puppeteer" rel="noopener noreferrer"&gt;
        puppeteer
      &lt;/a&gt; / &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;
        puppeteer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JavaScript API for Chrome and Firefox
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Puppeteer&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/puppeteer/puppeteer/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/puppeteer/puppeteer/actions/workflows/ci.yml/badge.svg?branch=main" alt="build"&gt;&lt;/a&gt;
&lt;a href="https://npmjs.org/package/puppeteer" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1d876566818989a769a171c218f8dbe53d85eb0394a9d5619159cd158c2b1fd5/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f7075707065746565722e737667" alt="npm puppeteer package"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F10379601%2F29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Puppeteer is a JavaScript library which provides a high-level API to control
Chrome or Firefox over the
&lt;a href="https://chromedevtools.github.io/devtools-protocol/" rel="nofollow noopener noreferrer"&gt;DevTools Protocol&lt;/a&gt; or &lt;a href="https://pptr.dev/webdriver-bidi" rel="nofollow noopener noreferrer"&gt;WebDriver BiDi&lt;/a&gt;
Puppeteer runs in the headless (no visible UI) by default&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;
&lt;a href="https://pptr.dev/docs" rel="nofollow noopener noreferrer"&gt;Get started&lt;/a&gt; | &lt;a href="https://pptr.dev/api" rel="nofollow noopener noreferrer"&gt;API&lt;/a&gt; | &lt;a href="https://pptr.dev/faq" rel="nofollow noopener noreferrer"&gt;FAQ&lt;/a&gt; | &lt;a href="https://pptr.dev/contributing" rel="nofollow noopener noreferrer"&gt;Contributing&lt;/a&gt; | &lt;a href="https://pptr.dev/troubleshooting" rel="nofollow noopener noreferrer"&gt;Troubleshooting&lt;/a&gt;
&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm i puppeteer &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Downloads compatible Chrome during installation.&lt;/span&gt;
npm i puppeteer-core &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Alternatively, install as a library, without downloading Chrome.&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Example&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;puppeteer&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;'puppeteer'&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-c"&gt;// Or import puppeteer from 'puppeteer-core';&lt;/span&gt;
&lt;span class="pl-c"&gt;// Launch the browser and open a new blank page&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;browser&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;puppeteer&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;launch&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;browser&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;newPage&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Navigate the page to a URL.&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;goto&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'https://developer.chrome.com/'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Set screen size.&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;setViewport&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;width&lt;/span&gt;: &lt;span class="pl-c1"&gt;1080&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;height&lt;/span&gt;: &lt;span class="pl-c1"&gt;1024&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Type into search box using accessible&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Just an example of testing the pagination component using Jest.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&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;preact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Pagination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;generateMidPageTabValues&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;../../src/components/pagination-component&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;spy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;Pagination /&amp;gt; e2e tests&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;let&lt;/span&gt; &lt;span class="nx"&gt;scratch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&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;scratch&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;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;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Pagination&lt;/span&gt; &lt;span class="nx"&gt;initialPage&lt;/span&gt;&lt;span class="o"&gt;=&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="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;pageChange&lt;/span&gt;&lt;span class="o"&gt;=&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;to&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;spy&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;to&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="nx"&gt;totalItems&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;, scratch&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should display proper information about pages&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scratch&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="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Showing items 1 to 15 of 45&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shouldn't allow to decrease current page if we're on the first page&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevAllowed&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should properly increase current page&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;done&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;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pageForward&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="nx"&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scratch&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="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Showing items 16 to 30 of 45&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenLastCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should correctly compute first page indices&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should correctly compute last page indices&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should correctly compute middle pages indices&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should correctly compute indices when few pages&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&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="mi"&gt;5&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="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateMidPageTabValues&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="mi"&gt;5&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="nf"&gt;toEqual&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;The building, testing and deploying process is automated using &lt;a href="https://jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt; , with the tests running in a docker container in order to perform the tests in an environment with all the graphical libraries puppeteer requires. The &lt;strong&gt;fairly simple&lt;/strong&gt; pipeline can be seen in Figure 9:&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%2F7pqqoo3pl821zo3jo973.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%2F7pqqoo3pl821zo3jo973.png" alt="Jenkins Pipeline" width="724" height="89"&gt;&lt;/a&gt;&lt;br&gt;Figure 9: Jenkins pipeline for deploying the front-end&lt;br&gt;

 &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;We build the binaries, perform the unit tests and then deploy to a testing dominion which varies based on the git branch we are on. Then we perform the e2e tests on that dominion.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The docker image used for the container in which Jenkins runs the pipeline is a custom version of the &lt;a href="https://github.com/alekzonder/docker-puppeteer" rel="noopener noreferrer"&gt;Puppeteer Docker Image&lt;/a&gt; , based on the ubuntu 16.04 image with node 8. The dockerfile can be seen below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;We add the “deployuser” to the container because Jenkins mounts its workspace directory (owned by deployuser) and it requires the user inside the container and outside to be the same in order to perform all the operations required for the deployment to be succesful.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This way, we avoid the whole application to suddenly crash and burn or turn into &lt;a href="http://www.adamhammond.com/90s/radiohead/" rel="noopener noreferrer"&gt;one of those amazing 90’s websites&lt;/a&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In this article we have presented an overview of the technology stack we used for rebuilding the QMENTA front-end and how it focuses on being small, efficient and simple. We have also seen how we use decorators in our project and the different parts that compound it such as API calls, routing, state management etc.&lt;/p&gt;

&lt;p&gt;By setting the foundations and patterns of the project, the next steps are continuously improving the new front-end in a scalable and maintainable way and adding meaningful features to match the functionality of the old platform, while keeping it minimal. &lt;a href="https://preview.qmenta.com/#/login" rel="noopener noreferrer"&gt;You can see how it goes here&lt;/a&gt;. 👋&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Special kudos 🙌 to &lt;div class="ltag__user ltag__user__id__20729"&gt;
    &lt;a href="/k1r0s" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&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%2Fuser%2Fprofile_image%2F20729%2F4fe91b05-2722-48b2-a40c-f7a7de8fb170.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%2Fuser%2Fprofile_image%2F20729%2F4fe91b05-2722-48b2-a40c-f7a7de8fb170.jpg" alt="k1r0s image"&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/k1r0s"&gt;Ciro Ivan&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/k1r0s"&gt;/k1r0s&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
 for setting the foundations of the project, carrying the heavyweight through most of it and teaching me the ways of the H̶a̶c̶k̶e̶r̶m̶a̶n̶ Javascript ninja.&lt;/em&gt; 🤖 🏋🏻&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Want to experiment with these technologies in your next projects? Yay! Here you have some boilerplates we created in github to get you up and running as soon as possible.🤘🏼&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aalises/preact-typescript-parcel-starter" rel="noopener noreferrer"&gt;Preact + Typescript + Parcel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/aalises" rel="noopener noreferrer"&gt;
        aalises
      &lt;/a&gt; / &lt;a href="https://github.com/aalises/preact-typescript-parcel-starter" rel="noopener noreferrer"&gt;
        preact-typescript-parcel-starter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Starter with Preact - Typescript - Parcel Bundler
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Preact + Typescript + Parcel Starter&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Zero configuration bundling and lightweight state management and view =&amp;gt; Preact + Typescript + Parcel Bundler + Redux Zero + Jest + Pupetteer&lt;/h4&gt;
&lt;/div&gt;
&lt;p&gt;This starter project contains many features and technologies to start the development ASAP.&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Microsoft/TypeScript" rel="noopener noreferrer"&gt;TypeScript:&lt;/a&gt; Strong typing and the future of JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/developit/preact" rel="noopener noreferrer"&gt;Preact:&lt;/a&gt; dom manipulation + components&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/concretesolutions/redux-zero" rel="noopener noreferrer"&gt;Redux-Zero:&lt;/a&gt; state management&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/k1r0s/preact-bind-group" rel="noopener noreferrer"&gt;Preact-Bind-Group:&lt;/a&gt; Group Form fields onChange Events to a single Callback, making forms easier to manage&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/parcel-bundler/parcel" rel="noopener noreferrer"&gt;Parcel:&lt;/a&gt; 0 configuration bundler&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/facebook/jest" rel="noopener noreferrer"&gt;Jest:&lt;/a&gt; Delightful javascript testing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/GoogleChrome/puppeteer" rel="noopener noreferrer"&gt;Puppeteer:&lt;/a&gt; Headless chrome node API scrapper&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/saadq/lynt" rel="noopener noreferrer"&gt;Lynt:&lt;/a&gt; 0 configuration linting for TS + React&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;run development server &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/h4&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;build the project (on /dist) &lt;code&gt;npm run build&lt;/code&gt;
&lt;/h4&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;run tests &lt;code&gt;npm run test&lt;/code&gt;
&lt;/h4&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;run linting &lt;code&gt;npm run lint&lt;/code&gt;
&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;The starter includes a basic sample application showcasing the different technologies using &lt;a href="https://bulma.io/" rel="nofollow noopener noreferrer"&gt;Bulma&lt;/a&gt; for styles. The example consists on a restaurant order making  form with validation/model and some global state…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/aalises/preact-typescript-parcel-starter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/k1r0s/preact-poi-starter" rel="noopener noreferrer"&gt;Preact + Poi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/k1r0s" rel="noopener noreferrer"&gt;
        k1r0s
      &lt;/a&gt; / &lt;a href="https://github.com/k1r0s/preact-poi-starter" rel="noopener noreferrer"&gt;
        preact-poi-starter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Preact Example App powered by Poi and BabelJS
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Preact Poi starter&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;So whats the deal here?&lt;/p&gt;
&lt;p&gt;Poi justs works. Of course Poi will try to look up a &lt;code&gt;.babelrc&lt;/code&gt;. Since Preact has a different &lt;strong&gt;jsx pragma&lt;/strong&gt; you must have &lt;strong&gt;transform-react-jsx&lt;/strong&gt; plugin with &lt;code&gt;{ pragma: "h" }&lt;/code&gt; opt at least to work with.&lt;/p&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"sourceMaps"&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"presets"&lt;/span&gt;: [
    [&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;es2015&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, { &lt;span class="pl-ent"&gt;"loose"&lt;/span&gt;:&lt;span class="pl-c1"&gt;true&lt;/span&gt; }],
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;stage-0&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  ],
  &lt;span class="pl-ent"&gt;"plugins"&lt;/span&gt;: [
    [&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;transform-decorators-legacy&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;],
    [&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;transform-react-jsx&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, { &lt;span class="pl-ent"&gt;"pragma"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;h&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; }]
  ]
}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;For this example I added &lt;code&gt;transform-decorators-legacy&lt;/code&gt; as well.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;run development server&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt; aka &lt;code&gt;$ poi [root file]&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;run production build&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt; aka &lt;code&gt;$ poi build [root file]&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/k1r0s/preact-poi-starter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/k1r0s/bleeding-preact-starter" rel="noopener noreferrer"&gt;Preact + Typescript + Webpack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/k1r0s" rel="noopener noreferrer"&gt;
        k1r0s
      &lt;/a&gt; / &lt;a href="https://github.com/k1r0s/bleeding-preact-starter" rel="noopener noreferrer"&gt;
        bleeding-preact-starter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      TypeScript · Preact · Redux-zero =&amp;gt; frontend starter with bleeding features. Check it out!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Bleeding Frontend Starter&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Preact + TypeScript + Redux-Zero + Moar..&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;This starter project contains many features to speedup development and &lt;strong&gt;have fun&lt;/strong&gt; which is pretty handy today.&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Microsoft/TypeScript" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; Strong typing and the future of JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/developit/preact" rel="noopener noreferrer"&gt;Preact&lt;/a&gt; dom manipulation + components&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/concretesolutions/redux-zero" rel="noopener noreferrer"&gt;Redux-Zero&lt;/a&gt; state management&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/k1r0s/preact-routlet" rel="noopener noreferrer"&gt;Preact-Routlet&lt;/a&gt; Simple Component drive Routing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/k1r0s/http-decorator" rel="noopener noreferrer"&gt;Http-Decorator&lt;/a&gt; Wrap your components with axios, no more async management&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/k1r0s/preact-stylesheet-decorator" rel="noopener noreferrer"&gt;Preact-Stylesheet-Decorator&lt;/a&gt; Wrap your components with scoped css&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/k1r0s/preact-bind-group" rel="noopener noreferrer"&gt;Preact-Bind-Group&lt;/a&gt; Group Form fields onChange Events to a single Callback&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;common&lt;/code&gt; folder contains Connect decorator for redux-zero (which is not yet included on the lib)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Powered by &lt;a href="https://github.com/webpack/webpack" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Heading production env -&amp;gt; Polyfills, Shims &amp;amp; Extensions&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;I recommend to install core-js and import all its contents on your main file &lt;code&gt;import 'core-js'&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/k1r0s/bleeding-preact-starter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>preact</category>
      <category>typescript</category>
      <category>parcel</category>
      <category>redux</category>
    </item>
  </channel>
</rss>
