<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrei Rusu</title>
    <description>The latest articles on DEV Community by Andrei Rusu (@andreirusu_).</description>
    <link>https://dev.to/andreirusu_</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%2F138453%2Ff5da33bb-0c1f-441d-9e6b-9a76bed2e537.jpg</url>
      <title>DEV Community: Andrei Rusu</title>
      <link>https://dev.to/andreirusu_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andreirusu_"/>
    <language>en</language>
    <item>
      <title>Test your React Components with Nightwatch and Testing Library</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Sun, 08 Jan 2023 16:46:28 +0000</pubDate>
      <link>https://dev.to/andreirusu_/test-your-react-components-with-nightwatch-and-testing-library-5f3g</link>
      <guid>https://dev.to/andreirusu_/test-your-react-components-with-nightwatch-and-testing-library-5f3g</guid>
      <description>&lt;p&gt;Component testing in Nightwatch has been refined with version 2.4 and support for testing React components (via the &lt;code&gt;@nightwatch/react&lt;/code&gt; plugin) has been significantly improved. We also released a new plugin for using the popular &lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt; with Nightwatch - &lt;code&gt;@nightwatch/testing-library&lt;/code&gt;, available since Nightwatch v2.6.&lt;/p&gt;

&lt;p&gt;We're now going to build a detailed example of how to use Nightwatch and Testing Library to test React components. We'll be &lt;a href="https://github.com/testing-library/react-testing-library#complex-example" rel="noopener noreferrer"&gt;Complex example&lt;/a&gt; available on the &lt;a href="https://www.npmjs.com/package/@testing-library/react" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt; docs, which is written with &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll cover how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;set up a new React project with &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;, which is also what Nightwatch uses internally for component testing;&lt;/li&gt;
&lt;li&gt;install and configure Nightwatch and Testing Library;&lt;/li&gt;
&lt;li&gt;mock API requests using the &lt;code&gt;@nightwatch/api-testing&lt;/code&gt; plugin;&lt;/li&gt;
&lt;li&gt;write a complex React component test using Nightwatch and Testing Library.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 0. Create a new project
&lt;/h2&gt;

&lt;p&gt;To get started, we'll create a new project with Vite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select &lt;code&gt;React&lt;/code&gt; and &lt;code&gt;JavaScript&lt;/code&gt; when prompted. This will create a new project with React and JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. Install Nightwatch and Testing Library
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Testing Library&lt;/strong&gt; for React can be installed with the &lt;code&gt;@testing-library/react&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @testing-library/react &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To install Nightwatch, run the init command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init nightwatch@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select &lt;code&gt;Component testing&lt;/code&gt; and &lt;code&gt;React&lt;/code&gt; when prompted. This will install &lt;code&gt;nightwatch&lt;/code&gt; and the &lt;code&gt;@nightwatch/react&lt;/code&gt; plugin. Choose a browser to install the driver for. We'll be using Chrome in this example.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1. Install @nightwatch/testing-library plugin
&lt;/h3&gt;

&lt;p&gt;Since v2.6, Nightwatch provides its own plugin for using the Testing Library queries directly as commands. We're going to need it to write our test later on so let's install it now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @nightwatch/testing-library &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Install @nightwatch/apitesting plugin
&lt;/h3&gt;

&lt;p&gt;The example contains a mock server that is needed to test the component. We'll be using the integrated mock server that comes with the &lt;code&gt;@nightwatch/apitesting&lt;/code&gt; plugin. Install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @nightwatch/apitesting &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2. Create the Login component
&lt;/h2&gt;

&lt;p&gt;We'll use the same component as in the React Testing Library docs. Create a new file &lt;code&gt;src/Login.jsx&lt;/code&gt; and add the following code:&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="c1"&gt;// login.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Login&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="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;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&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;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolved&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;loading&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;error&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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;usernameInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passwordInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&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;loading&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="na"&gt;resolved&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;error&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="nb"&gt;window&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/login&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&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;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;usernameInput&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="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;passwordInput&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="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;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&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="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;user&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;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;loading&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;resolved&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="na"&gt;error&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;error&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;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;loading&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;resolved&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;error&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="nx"&gt;message&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="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;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&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;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;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"usernameInput"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Username&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&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;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"usernameInput"&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"passwordInput"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&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;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"passwordInput"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&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;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="si"&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;loading&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="kc"&gt;null&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;button&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;form&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;state&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"alert"&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&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;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&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;resolved&lt;/span&gt; &lt;span class="p"&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;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Congrats! You're signed in!&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="kc"&gt;null&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;)&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="nx"&gt;Login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3. Create the component test
&lt;/h2&gt;

&lt;p&gt;One of the founding principles of Testing Library is that tests should resemble how users interact with the application as much as possible. When writing component tests in Nightwatch using JSX, we need to write the test as a component story using the &lt;a href="https://storybook.js.org/docs/react/api/csf" rel="noopener noreferrer"&gt;Component Story Format&lt;/a&gt;, a declarative format introduced by Storybook.&lt;/p&gt;

&lt;p&gt;This enables us to write tests that focus on how the component is used, rather than how it's implemented, which is in line with the Testing Library philosophy. You can read more about this in the &lt;a href="https://nightwatchjs.org/guide/component-testing/write-jsx-react-tests.html" rel="noopener noreferrer"&gt;Nightwatch docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The great thing about using this format to write our tests is that we can use the same code to write stories for our components, which can be used to document and showcase them in &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Login with valid credentials test
&lt;/h3&gt;

&lt;p&gt;Create a new file &lt;code&gt;src/Login.spec.jsx&lt;/code&gt; and add the following code, which does the same as the &lt;a href="https://github.com/testing-library/react-testing-library#complex-example" rel="noopener noreferrer"&gt;complex example&lt;/a&gt; written with Jest:&lt;/p&gt;

&lt;p&gt;To render the component using JSX in Nightwatch, we simply create an export for the rendered component, optionally with a set of props. The &lt;code&gt;play&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; functions are used to interact with the component and verify the results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;play&lt;/code&gt; is used to interact with the component. It's executed in the browser context, so we can use the &lt;code&gt;screen&lt;/code&gt; object from Testing Library to query the DOM and fire events;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test&lt;/code&gt; is used to verify the results. It's executed in the Node.js context, so we can use the Nightwatch &lt;code&gt;browser&lt;/code&gt; object to query the DOM and verify the results.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login.spec.jsx&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;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&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;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Login&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/login&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Login&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;LoginWithValidCredentials&lt;/span&gt; &lt;span class="o"&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Login&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="nx"&gt;LoginWithValidCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;canvasElement&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//fill out the form&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;LoginWithValidCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// verify the results&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the mock server
&lt;/h3&gt;

&lt;p&gt;The example uses a mock server to simulate a login request. We'll be using the integrated mock server that comes with the &lt;code&gt;@nightwatch/apitesting&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;For this we'll use the &lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;teardown&lt;/code&gt; hooks which we can write directly in the test file. Both hooks are executed in the Node.js context.&lt;/p&gt;

&lt;p&gt;We also need to set login endpoint to &lt;code&gt;http://localhost:3000/api/login&lt;/code&gt; in the &lt;code&gt;Login&lt;/code&gt; component, which is the url to the mock server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complete test file
&lt;/h3&gt;

&lt;p&gt;The complete test file will look like this:&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="c1"&gt;// login.spec.jsx&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;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&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;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Login&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/Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;server&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fake_user_token&lt;/span&gt;&lt;span class="dl"&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;serverResponse&lt;/span&gt; &lt;span class="o"&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;token&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;mockserver&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;server&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;mockserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;app&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="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="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serverResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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;serverResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockServerPort&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;teardown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&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;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoginWithValidCredentials&lt;/span&gt; &lt;span class="o"&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Login&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="nx"&gt;LoginWithValidCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;canvasElement&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//fill out the form&lt;/span&gt;
  &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/username/i&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chuck&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;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/password/i&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;norris&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;fireEvent&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="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/submit/i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;LoginWithValidCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&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="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/congrats/i&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;localStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&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;await&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;localStorage&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="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakeUserResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&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;
  
  
  Debugging
&lt;/h3&gt;

&lt;p&gt;One of the main benefits of using Nightwatch for component testing, besides having the same API available for end-to-end testing, is that we can run the tests in a real browser, instead of a virtual DOM environment, such as JSDOM.&lt;/p&gt;

&lt;p&gt;This allows us to use the Chrome Dev Tools to debug the tests.&lt;/p&gt;

&lt;p&gt;For example, let's go ahead and add a &lt;code&gt;debugger&lt;/code&gt; statement in the &lt;code&gt;LoginWithValidCredentials.play&lt;/code&gt; function:&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;LoginWithValidCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;canvasElement&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//fill out the form&lt;/span&gt;
  &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/username/i&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chuck&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;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/password/i&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;norris&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;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;fireEvent&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="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/submit/i&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;Now, let's run the test with &lt;code&gt;--debug&lt;/code&gt; and &lt;code&gt;--devtools&lt;/code&gt; flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/login.spec.jsx &lt;span class="nt"&gt;--debug&lt;/span&gt; &lt;span class="nt"&gt;--devtools&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open a new Chrome window with the Dev Tools open. We can now set a breakpoint in the Dev Tools and step through the code.&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%2Fh32pqi15tbd1id15vrkb.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%2Fh32pqi15tbd1id15vrkb.png" alt="Debugging" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Login with server exception test
&lt;/h3&gt;

&lt;p&gt;The original &lt;a href="https://github.com/testing-library/react-testing-library#complex-example" rel="noopener noreferrer"&gt;example&lt;/a&gt; from the TestingLibrary docs also includes a test for the case when the server throws an exception.&lt;/p&gt;

&lt;p&gt;Let's try to write the same in Nightwatch. This time we'll use just the &lt;code&gt;test&lt;/code&gt; function, since we can interact with the component this way as well. As we've mentioned earlier, the &lt;code&gt;test&lt;/code&gt; function is executed in the Node.js context, and it receives the Nightwatch &lt;code&gt;browser&lt;/code&gt; object as argument.&lt;/p&gt;

&lt;p&gt;We'll also need to update the mock server response to return a 500 status code and an error message. This we can easily accomplish by writing a &lt;code&gt;preRender&lt;/code&gt; test hook on the &lt;code&gt;LoginWithServerException&lt;/code&gt; component story.&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoginWithServerException&lt;/span&gt; &lt;span class="o"&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Login&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="nx"&gt;LoginWithServerException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preRender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&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;serverResponse&lt;/span&gt; &lt;span class="o"&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;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&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="s1"&gt;Internal server error&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="nx"&gt;LoginWithServerException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/username/i&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;username&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chuck&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;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/password/i&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;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;norris&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/submit/i&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;submit&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&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="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/internal server error/i&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;localStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&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;await&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;localStorage&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="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Run the test
&lt;/h2&gt;

&lt;p&gt;Finally, let's run the test. This will run the &lt;code&gt;LoginWithValidCredentials&lt;/code&gt; and &lt;code&gt;LoginWithServerException&lt;/code&gt; component stories in Chrome.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/login.spec.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the test without opening the browser, we can pass the &lt;code&gt;--headless&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;If all goes well, you should see the following output:&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="o"&gt;[&lt;/span&gt;Login] Test Suite
────────────────────────────────────
ℹ Connected to ChromeDriver on port 9515 &lt;span class="o"&gt;(&lt;/span&gt;1134ms&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  Using: chrome &lt;span class="o"&gt;(&lt;/span&gt;108.0.5359.124&lt;span class="o"&gt;)&lt;/span&gt; on MAC OS X.

Mock server listening on port 3000

  Running &amp;lt;LoginWithValidCredentials&amp;gt; component:
──────────────────────────────────────────────────────────────
&lt;span class="o"&gt;[&lt;/span&gt;browser] &lt;span class="o"&gt;[&lt;/span&gt;vite] connecting...
&lt;span class="o"&gt;[&lt;/span&gt;browser] &lt;span class="o"&gt;[&lt;/span&gt;vite] connected.
  ✔ Expected element &amp;lt;LoginWithValidCredentials&amp;gt; to be visible &lt;span class="o"&gt;(&lt;/span&gt;15ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ Expected element &amp;lt;DIV[id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; DIV &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; DIV&amp;gt; text to match: &lt;span class="s2"&gt;"/congrats/i"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;14ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ Expected &lt;span class="s1"&gt;'fake_user_token'&lt;/span&gt;  to equal&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fake_user_token'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: 

  ✨ PASSED. 3 assertions. &lt;span class="o"&gt;(&lt;/span&gt;1.495s&lt;span class="o"&gt;)&lt;/span&gt;

  Running &amp;lt;LoginWithServerException&amp;gt; component:
──────────────────────────────────────────────────────────────
&lt;span class="o"&gt;[&lt;/span&gt;browser] &lt;span class="o"&gt;[&lt;/span&gt;vite] connecting...
&lt;span class="o"&gt;[&lt;/span&gt;browser] &lt;span class="o"&gt;[&lt;/span&gt;vite] connected.
  ✔ Expected element &amp;lt;LoginWithServerException&amp;gt; to be visible &lt;span class="o"&gt;(&lt;/span&gt;8ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ Expected element &amp;lt;DIV[id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; DIV &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; DIV&amp;gt; text to match: &lt;span class="s2"&gt;"/internal server error/i"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;8ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ Expected &lt;span class="s1"&gt;'fake_user_token'&lt;/span&gt;  to equal&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fake_user_token'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: 

  ✨ PASSED. 3 assertions. &lt;span class="o"&gt;(&lt;/span&gt;1.267s&lt;span class="o"&gt;)&lt;/span&gt;

  ✨ PASSED. 6 total assertions &lt;span class="o"&gt;(&lt;/span&gt;4.673s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it! You can find the complete code for this example in the &lt;a href="https://github.com/nightwatchjs-community/testing-library-example" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. PRs are welcome.&lt;/p&gt;

&lt;p&gt;Fell free to drop by the &lt;a href="https://discord.com/invite/SN8Da2X" rel="noopener noreferrer"&gt;Nightwatch Discord&lt;/a&gt; if you have any questions or feedback.     &lt;/p&gt;

</description>
      <category>programming</category>
      <category>solana</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Create, Test and Deploy a Single Page App with Vue 3 + Vite and Pinia</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Mon, 07 Feb 2022 08:56:51 +0000</pubDate>
      <link>https://dev.to/andreirusu_/create-test-and-deploy-a-single-page-app-with-vue-3-vite-and-pinia-5097</link>
      <guid>https://dev.to/andreirusu_/create-test-and-deploy-a-single-page-app-with-vue-3-vite-and-pinia-5097</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Created in 2014, &lt;a href="https://v3.vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt; is undoubtedly one of the leading frontend frameworks at the moment and with a growing community and expanding ecosystem it seems that its position is firm for quite some time. I have worked with Vue 2 several years ago for a few projects and I found it a delightful experience.&lt;/p&gt;

&lt;p&gt;I thought now it’s the time to upgrade my tool-set with the latest version and also with newer tools like &lt;a href="https://labs.pineview.io/learn-how-to-build-test-and-deploy-a-single-page-app-with-vue-3-vite-and-pinia/vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; and &lt;a href="https://pinia.vuejs.org/" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This guide will cover in detail the steps to create a functional example bookstore single page application using Vue 3 and run it using Vite. It also includes details on how to add state management using Pinia (the &lt;a href="https://vuex.vuejs.org/" rel="noopener noreferrer"&gt;Vuex&lt;/a&gt; successor) and routing using &lt;a href="https://router.vuejs.org/" rel="noopener noreferrer"&gt;Vue Router&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core concepts that will be covered are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a &lt;strong&gt;Vue 3&lt;/strong&gt; single page application using &lt;strong&gt;Vite&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;managing routes with &lt;strong&gt;Vue Router&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;managing application state with &lt;strong&gt;Pinia&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;running, building and deploying the app with &lt;strong&gt;Vite&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;writing and running &lt;strong&gt;Vue component tests&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;writing and running automated end-to-end tests with &lt;a href="https://nightwatchjs.org/" rel="noopener noreferrer"&gt;Nightwatch.js&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may seem like a lot but I think it’s perfectly possible to go through all of it in less than 20 minutes. Some of the topics listed above could be expanded into entire tutorials of their own, but for now I am covering only what’s needed to have everything up and running.&lt;/p&gt;

&lt;p&gt;One last thing that needs to be mentioned is that the backend is not covered in this tutorial. There is no server side component per se, although the data is loaded using the browser Fetch API (the successor of XHR) so a backend component could be easily added.&lt;/p&gt;

&lt;p&gt;For all accounts and purposes, the application we’ll build here can be deployed as a static website. If you're eager to get right down to coding and you'd like to jump into it right away, you can just get the project up and running with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/beatfactor/middlemarch
npm install
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or fork the project on Github at: &lt;a href="https://github.com/beatfactor/middlemarch" rel="noopener noreferrer"&gt;https://github.com/beatfactor/middlemarch&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Step 1 – Setting Up the Application with the &lt;code&gt;create-vite&lt;/code&gt; Scaffolding Tool
&lt;/h2&gt;

&lt;p&gt;We’re going to use the official create-vite scaffolding tool to setup the project structure so make sure you have Node 12+ installed with NPM 6+. They also support Yarn and PNPM as package managers, but we’ll only cover NPM.&lt;/p&gt;

&lt;p&gt;The create-vite tool will also create the project folder for you, so just make sure to cd into the parent folder first: &lt;code&gt;cd ~/workspace&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;Vite&lt;/code&gt; and initialise the project with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you’ll be prompted to enter the project name and select the library which you want to use. From the list, choose &lt;code&gt;vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/workspace % npm init vite@latest
npx: installed 6 &lt;span class="k"&gt;in &lt;/span&gt;1.051s
✔ Project name: … vue-bookstore
? Select a framework: › - Use arrow-keys. Return to submit.
    vanilla
❯   vue
    react
    preact
    lit
    svelte 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then select &lt;code&gt;vue&lt;/code&gt; as the variant, since we’ll not be using TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Select a variant: › - Use arrow-keys. Return to submit.
❯   vue
    vue-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx: installed 6 &lt;span class="k"&gt;in &lt;/span&gt;1.051s
✔ Project name: … vue-bookstore
✔ Select a framework: › vue
✔ Select a variant: › vue

Scaffolding project &lt;span class="k"&gt;in&lt;/span&gt; /Users/andrei/workspace/vue-bookstore...

Done. Now run:

  &lt;span class="nb"&gt;cd &lt;/span&gt;vue-bookstore
  npm &lt;span class="nb"&gt;install
  &lt;/span&gt;npm run dev 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have followed the above instructions, we’ll get the following output from Vite telling us that the app is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  vite v2.7.7 dev server running at:

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Local: http://localhost:3000/
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Network: use &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; to expose

  ready &lt;span class="k"&gt;in &lt;/span&gt;611ms.

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

&lt;/div&gt;



&lt;p&gt;Let’s visit the localhost:3000 url. The welcome page looks like this:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Step 2 – Adding routing with Vue Router and state management with Pinia
&lt;/h2&gt;

&lt;p&gt;Let’s review the project’s directory structure created by the &lt;code&gt;create-vite&lt;/code&gt; tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vue-bookstore/
 ├── public/
 |    ├── favicon.ico
 ├── src/
 |    ├── assets/
 |    |    └── logo.png
 |    ├── components/
 |    |    └── HelloWorld.vue
 |    ├── App.vue
 |    └── main.js
 ├─── package.json
 ├─── README.md
 └─── vite.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this section of our guide, we’ll be adding two new dependencies to our project: &lt;strong&gt;vue-router&lt;/strong&gt; and &lt;strong&gt;pinia&lt;/strong&gt;. Let’s go ahead and install them from NPM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vue Router
&lt;/h3&gt;

&lt;p&gt;Vue Router is the official router for Vue.js. We’ll need to install version 4 which is compatible with Vue 3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vue-router@4 &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pinia
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pinia.vuejs.org/" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt; is one of the newest projects to emerge from the Vue ecosystem and it’s the new official state management tool for Vue.js apps. It’s api is very similar to Vuex (its predecessor) and it is designed to be faster and more lightweight.&lt;/p&gt;

&lt;p&gt;You can install pinia from NPM with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;pinia &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Setting Up Routing
&lt;/h3&gt;

&lt;p&gt;If you’re unfamiliar with routing in a single page application or state management, don’t worry; both of these concepts are very easy to understand and they will be self explained once you see how it works.&lt;/p&gt;

&lt;p&gt;Also, remember that we are just building a tutorial here and the goal is to have everything up and running in 20 minutes and that doesn’t require learning all there is to know about Vue.js. It doesn’t even require understanding everything we’ll be doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Single Page Application?
&lt;/h3&gt;

&lt;p&gt;Since we’re building a single page application here, it might be useful (though not essential) to consider what that means and why is it single page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A single page application is simply a web application that doesn’t reload the page when you navigate to another of its sub-pages. The url of the browser is modified though, as if the page has been reloaded, and that's done using the HTML5 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/History" rel="noopener noreferrer"&gt;History API&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Working with Vue Components in Vite
&lt;/h3&gt;

&lt;p&gt;The scaffolding created using the &lt;code&gt;create-vite&lt;/code&gt; tool adds a very basic Vue component, located in &lt;code&gt;src/components/HelloWorld.vue&lt;/code&gt;. It is then being used in the main application component, located in &lt;code&gt;src/App.vue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are two other important files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;src/main.js&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The index.html file is what the browser sees when it navigates to our application’s page and the main.js is the entry point for the Vue.js app.&lt;/p&gt;

&lt;p&gt;Here’s how these files look like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vite App&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/src/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;src/main.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApp&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&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;h3&gt;
  
  
  Adding Routes
&lt;/h3&gt;

&lt;p&gt;It’s time now to create our application’s main routes. In Vue, every route must correspond to a component. For this application, we’ll consider a component per subpage, like so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Homepage&lt;/strong&gt; - our bookstore homepage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cart&lt;/strong&gt; - the shopping cart and check out page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign-In&lt;/strong&gt; - the user sign-in page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since this is only an example, other pages like user sign-up or product detail page, have been left out. Also, the sign-in page is only contains only a mock sign-in.&lt;/p&gt;

&lt;p&gt;For basic HTML and CSS, I’ve also used &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap 5&lt;/a&gt; for things like UI dropdowns and forms, but of course you can use whatever UI library you want.&lt;/p&gt;

&lt;p&gt;We’ll create the page components empty for now so we can setup the routing. The new src directory structure will look like this (after removing the boilerplate code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
  ├── components/
  |    └── TopNavbar.js
  ├── lib/
  |    ├── router.js   
  |    └── store.js
  ├── pages/
  |    ├── cart/
  |    |    ├── cart.css
  |    |    ├── cart.html
  |    |    └── Cart.vue
  |    ├── home/
  |    |    ├── home.css
  |    |    ├── home.html
  |    |    └── Home.vue
  |    ├── sign-in/
  |    |    ├── sign-in.css
  |    |    ├── sign-in.html
  |    |    └── SignIn.vue
  |    └── routes.js
  ├── App.vue
  └── main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve added three pages, each of which we’ll keep very basic. We’ll just add &lt;code&gt;TobNavbar&lt;/code&gt; component to make the navigation working without page reloads.&lt;/p&gt;

&lt;p&gt;Add the following for &lt;code&gt;src/pages/cart/Cart.vue&lt;/code&gt;, &lt;code&gt;src/pages/home/Home.vue&lt;/code&gt; and &lt;code&gt;src/pages/sign-in/SignIn.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TopNavbar&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;../../components/TopNavbar.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TopNavbar&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;TopNavbar&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;

  &lt;span class="nf"&gt;mounted&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;data&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;};&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;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;TopNavbar&lt;/code&gt; component located in &lt;code&gt;src/components&lt;/code&gt; will contain just the navigation links. Notice the router-link component which is part of the &lt;code&gt;vue-router&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;to=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;to=&lt;/span&gt;&lt;span class="s"&gt;"/cart/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cart&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;to=&lt;/span&gt;&lt;span class="s"&gt;"/sign-in/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign In&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;pages/routes.js&lt;/code&gt; file contains all the route declarations for the application. Here’s how it looks like:&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;createRouter&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;vue-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Homepage&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;./home/Home.vue&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;SignIn&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;./sign-in/SignIn.vue&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;Cart&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;./cart/Cart.vue&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;routes&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="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Homepage&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sign-in/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignIn&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cart/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cart&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&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="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;routes&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;Before we’re ready to see the &lt;code&gt;vue-router&lt;/code&gt; in action we just need to do 2 more things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1)&lt;/strong&gt; Create the router and add it to the main Vue application instance, in &lt;code&gt;src/main.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApp&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createWebHistory&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;vue-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createRouter&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;./pages/routes.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createWebHistory&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&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;&lt;strong&gt;2)&lt;/strong&gt;Add the &lt;code&gt;&amp;lt;router-view&amp;gt;&lt;/code&gt; component in &lt;code&gt;src/App.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now re-run &lt;code&gt;npm run dev&lt;/code&gt; if needed and then navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; and you’ll have a routing enabled Vue 3 app.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Setting Up State Management Using Pinia
&lt;/h3&gt;

&lt;p&gt;Moving on, now we need to set up the Pinia store for our app. The store is where the application state is maintained.&lt;/p&gt;

&lt;p&gt;Pinia is a new project from the Vue.js core team and is now the recommended approach for working with the application state. If you’re already familiar with Vuex, getting used to Pinia will be straightforward. In fact, the Pinia api is slightly easier and less verbose than Vuex.&lt;/p&gt;

&lt;p&gt;With Pinia, in a Vue 3 app there is one root store and then any number of individual stores. For our bookstore app, we’re going to use only two stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;catalog&lt;/strong&gt; store: a list of available books&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;cart&lt;/strong&gt; store: books that the user wants to order&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating a Pinia
&lt;/h3&gt;

&lt;p&gt;A “pinia” is the root store that we have to create first and pass it to the Vue instance.&lt;/p&gt;

&lt;p&gt;We’ll do that in &lt;code&gt;src/main.js&lt;/code&gt; and update it to look like:&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;createApp&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createWebHistory&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;vue-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPinia&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;pinia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createRouter&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;./pages/routes.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPinia&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createWebHistory&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&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;Next step is to create the individual catalog and cart stores and use them in components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the Catalog Store
&lt;/h3&gt;

&lt;p&gt;Creating a Pinia store means two things mainly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;defining the store&lt;/li&gt;
&lt;li&gt;using the store in one or more components&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Defining the Store
&lt;/h4&gt;

&lt;p&gt;Like Vuex, the Pinia store contains the &lt;strong&gt;state&lt;/strong&gt; and two types of methods: &lt;strong&gt;getters&lt;/strong&gt; and &lt;strong&gt;actions&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Some things to consider about a store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Getters&lt;/code&gt; are synchronous functions used to retrieve data from the state &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Actions&lt;/code&gt; are functions that can also be asynchronous which are used to update the state&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;state&lt;/code&gt; is defined as a function returning the initial state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s time now to create the catalog store inside &lt;code&gt;src/stores/catalog.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineStore&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;pinia&lt;/span&gt;&lt;span class="dl"&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;useCatalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;catalog-store&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;state&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;newArrivals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;fetching&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="na"&gt;getters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;results&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="k"&gt;return&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;newArrivals&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="nf"&gt;isFetching&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="k"&gt;return&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;fetching&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="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetchNewArrivals&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="nx"&gt;fetching&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/data/new-arrivals.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newArrivals&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;books&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="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="nx"&gt;newArrivals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error loading new arrivals:&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetching&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the above source code, you’ll notice we have two getters (&lt;code&gt;results&lt;/code&gt; and &lt;code&gt;isFetching&lt;/code&gt;) and one action (&lt;code&gt;fetchNewArrivals&lt;/code&gt;). Instead of a real backend we just have a json file located in &lt;code&gt;/data/new-arrivals.json&lt;/code&gt; which contains a few books that we’ll use as our catalog.&lt;/p&gt;

&lt;p&gt;You’ll also notice that our getters don’t do anything special with the data and so they’re a bit unnecessary, but I thought  it’s still good to show how you can define them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the Store in a Template
&lt;/h4&gt;

&lt;p&gt;Linking the above definition to a template is also quite straightforward. &lt;/p&gt;

&lt;p&gt;Let’s create a new component called &lt;code&gt;NewArrivals&lt;/code&gt; inside &lt;code&gt;src/components/NewArrivals.vue&lt;/code&gt; which we’ll use it the &lt;code&gt;Home.vue&lt;/code&gt; page component.&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;&amp;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;useCatalog&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;../../store/catalog.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="nx"&gt;scoped&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;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;mapState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapActions&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;pinia&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;computed&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;mapState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;useCatalog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;newArrivals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;results&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;methods&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;mapActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;useCatalog&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="s1"&gt;fetchNewArrivals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;

    &lt;span class="nf"&gt;addToCart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// we'll populate this later&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="nf"&gt;created&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// when the template is created, we call this action&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;fetchNewArrivals&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;Home.vue&lt;/code&gt; component becomes:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TopNavbar&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;../../components/TopNavbar.vue&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;NewArrivals&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;../../components/NewArrivals.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TopNavbar&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NewArrivals&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;TopNavbar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;NewArrivals&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="nf"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="nf"&gt;data&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;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a diagram of how the store and the component work together in the application:&lt;/p&gt;

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

&lt;p&gt;I also wrote a store and a component for the cart but I will not include it in the tutorial because the mechanism is similar and you can inspect the source code in the repository which has everything added together, even some styles. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 – Testing Vue.js Components
&lt;/h2&gt;

&lt;p&gt;Component testing is a type of UI testing where the component is rendered in isolation, without the rest of app components, for the purpose of verifying its functionality. It’s usually a testing strategy which happens prior the end-to-end testing step, which we’ll elaborate in the next section.&lt;/p&gt;

&lt;p&gt;We need to install the &lt;a href="https://vue-test-utils.vuejs.org/" rel="noopener noreferrer"&gt;Vue TestUtils&lt;/a&gt; project, which is the official unit testing library for Vue.js and we need the one that targets Vue 3. You can install that from NPM with:&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="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;vue&lt;/span&gt;&lt;span class="sr"&gt;/test-utils@next --save-de&lt;/span&gt;&lt;span class="err"&gt;v
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Installing Nightwatch.js and ChromeDriver
&lt;/h3&gt;

&lt;p&gt;We’ll use Nightwatch.js for both component testing and end-to-end testing. Nightwatch is already one of the recommended testing frameworks by the Vue.js team and was published around the same time as Vue. &lt;/p&gt;

&lt;p&gt;It has recently gotten support (still in beta at the moment) for  Vue component testing through the &lt;a href="https://www.npmjs.com/package/vite-plugin-nightwatch" rel="noopener noreferrer"&gt;vite-plugin-nightwatch&lt;/a&gt;. We’ll go ahead an install Nightwatch v2 using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;nightwatch--save-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we’ll also need the &lt;code&gt;vite-plugin-nightwatch&lt;/code&gt; mentioned earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vite-plugin-nightwatch &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nightwatch uses the &lt;a href="https://w3c.github.io/webdriver/" rel="noopener noreferrer"&gt;W3C WebDriver API&lt;/a&gt; for browser automation tasks and we’ll need to install the &lt;code&gt;chromedriver&lt;/code&gt; NPM package as well, because we’re going to use Chrome to run our tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;chromedriver &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the &amp;lt;NewArrivals&amp;gt; Component
&lt;/h3&gt;

&lt;p&gt;And with that, we have arrived at the point where we can finally start writing the actual test for our NewArrivals component.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;vite-plugin-nightwatch&lt;/code&gt; mentioned earlier includes a test renderer page and Nightwatch already contains everything needed for running the initial test for our component.&lt;/p&gt;

&lt;p&gt;Create a folder &lt;code&gt;test&lt;/code&gt; and inside it two subfolders: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;component&lt;/code&gt; - this will hold component tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e2e&lt;/code&gt; - this will hold end-to-end tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also need a &lt;code&gt;nightwatch.conf.js&lt;/code&gt; configuration file, but we can run Nightwatch directly and the config file will be created for us automatically.  Just make sure &lt;code&gt;chromedriver&lt;/code&gt; is also installed (and the Chrome browser, of course).&lt;/p&gt;

&lt;p&gt;Make sure the current working directory is the project root and then simply run an &lt;a href="https://github.com/nightwatchjs/nightwatch/tree/main/examples/tests" rel="noopener noreferrer"&gt;example test&lt;/a&gt; which are bundled with Nightwatch. We’ll pick the &lt;code&gt;duckDuckGo&lt;/code&gt; test because it’s the fastest:&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="nv"&gt;$ &lt;/span&gt;npx nightwatch examples/tests/duckDuckGo.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project structure should look like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vue-bookstore/
 ├── public/
 |    ├── data/
 |    └── favicon.ico
 ├── src/
 ├── ...
 |    └── main.js
 ├── &lt;span class="nb"&gt;test&lt;/span&gt;/
 |    ├── component/
 |    └── e2e/
 ├─── nightwatch.conf.js
 ├─── package.json
 ├─── README.md
 └─── vite.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll go ahead and create a new file called &lt;code&gt;newArrivalsTest.js&lt;/code&gt; inside &lt;code&gt;test/component&lt;/code&gt;. In it, we’ll just add a basic test which mounts the component and checks if the returned element can be found in the page (i.e. the component has been mounted).&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="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="s1"&gt;New Arrivals Component Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&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="s1"&gt;checks if the component has been mounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mountVueComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/src/components/new-arrivals/NewArrivals.vue&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;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/src/lib/router.js&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;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;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;present&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;Nightwatch uses the same &lt;code&gt;describe()&lt;/code&gt; syntax as Mocha. You can even use Mocha as a test runner if you’re already familiar with it, but we’re not going to do that for now. In case you’d like to use Mocha, you only need to slip a few switches in the nightwatch config file and there’s documentation available on the &lt;a href="https://v2.nightwatchjs.org/guide/third-party-runners/using-mocha.html" rel="noopener noreferrer"&gt;Nightwatch website&lt;/a&gt; on how to do that. &lt;/p&gt;

&lt;p&gt;It’s time now to run the above test and for that we’ll run Nightwatch using Chrome, like so:&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="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;nightwatch&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&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;newArrivalsTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open the Chrome browser and render the component, then perform the test. If you don’t like seeing the browser window pop up during the test, you can pass the &lt;code&gt;--headless&lt;/code&gt; argument, like so:&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="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;nightwatch&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&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;newArrivalsTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;headless&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test output should look like below:&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="o"&gt;[&lt;/span&gt;New Arrivals Component Test] Test Suite
──────────────────────────────────────────────────────────────
ℹ Connected to ChromeDriver on port 9515 &lt;span class="o"&gt;(&lt;/span&gt;652ms&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  Using: chrome &lt;span class="o"&gt;(&lt;/span&gt;97.0.4692.99&lt;span class="o"&gt;)&lt;/span&gt; on MAC OS X.


  Running tests the component:
──────────────────────────────────────────────────────────────
  ✔ Expected element &amp;lt;web element&lt;span class="o"&gt;{&lt;/span&gt;e53f9b1e-11d3-4dc4-8728-4d3cd077343e&lt;span class="o"&gt;}&amp;gt;&lt;/span&gt; to be present &lt;span class="o"&gt;(&lt;/span&gt;1ms&lt;span class="o"&gt;)&lt;/span&gt;

OK. 1 assertions passed. &lt;span class="o"&gt;(&lt;/span&gt;781ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can of course consult all the CLI options that the Nightwatch runner provides, either by going to the &lt;a href="https://v2.nightwatchjs.org/guide/running-tests/command-line-options.html" rel="noopener noreferrer"&gt;docs pages&lt;/a&gt; or by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nightwatch &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Extending the &amp;lt;NewArrivals&amp;gt; Test
&lt;/h4&gt;

&lt;p&gt;You may have noticed that our component test isn’t testing that much which means that the test is not as helpful as it could be. So we’ll go ahead and extend it only a little bit.&lt;/p&gt;

&lt;p&gt;We’ll just inspect the &lt;code&gt;NewArrivals&lt;/code&gt; component and check if there is a property in it called &lt;code&gt;newArrivals&lt;/code&gt;, which is used in the html to render the results.&lt;/p&gt;

&lt;p&gt;The test looks like this now. We’ve refactored the component mounting into the the &lt;code&gt;before&lt;/code&gt; hook so we can only do the checks inside the test, which is the &lt;code&gt;it&lt;/code&gt; block. The &lt;code&gt;expect&lt;/code&gt; library is provided by Nightwatch out of the box and it is based on the popular and versatile &lt;a href="https://www.chaijs.com/api/bdd/" rel="noopener noreferrer"&gt;Chai.js&lt;/a&gt; assertion library. More info on how to use the &lt;code&gt;expect&lt;/code&gt; on the &lt;a href="https://nightwatchjs.org/api/expect/" rel="noopener noreferrer"&gt;Nightwatch docs&lt;/a&gt; website.&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="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="s1"&gt;New Arrivals Component Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&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;component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mountVueComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/src/components/new-arrivals/NewArrivals.vue&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;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/src/lib/router.js&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="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="s1"&gt;checks if the component has been mounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&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;browser&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;component&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;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;present&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;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newArrivals&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;component&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&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="s1"&gt;The Memory Police&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;elements&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.col-md-6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newArrivals&lt;/span&gt;&lt;span class="dl"&gt;'&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;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;an&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 – End-to-End Testing the Vue.js App
&lt;/h2&gt;

&lt;p&gt;We are nearing the end of this tutorial and before we can consider we have a working Vue.js app, we need to add support for end-to-end testing and setup a CI pipeline on Github Actions.&lt;/p&gt;

&lt;p&gt;Fortunately we don’t need to install, nor configure, any other tools, unless maybe some fancy reporters, but for now we can get everything we need in terms of end-to-end automated testing out of Nightwatch. Besides Chrome, Nightwatch has built-in support for all major browsers, including Firefox, Edge, and Safari, all thanks to its integration with the W3C Webdriver API and Selenium. It also allows you to use distributed cloud testing platforms like &lt;a href="https://www.browserstack.com/" rel="noopener noreferrer"&gt;BrowserStack&lt;/a&gt;, &lt;a href="https://saucelabs.com/" rel="noopener noreferrer"&gt;SauceLabs&lt;/a&gt;, &lt;a href="https://crossbrowsertesting.com/" rel="noopener noreferrer"&gt;CrossBrowserTesting&lt;/a&gt;, or &lt;a href="https://www.lambdatest.com/" rel="noopener noreferrer"&gt;LambdaTest&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;For now, we’ll just keep things less complex and we’ll only focus on writing a few basic automated tests and run them in Chrome, Firefox, and Safari.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the Homepage End-to-end Test
&lt;/h3&gt;

&lt;p&gt;Let’s get started with the homepage end-to-end test and create a new file under &lt;code&gt;test/e2e/homePageTest.js&lt;/code&gt;. The syntax is the same as for the component test, but for running the end-to-end tests we’ll use the compiled build of our application.&lt;/p&gt;

&lt;p&gt;We can of course run them against the dev build, but the established practice in software development, as far as I can tell, is to run the end-to-end tests in an environment which simulates the production as close as possible. Which is why they’re called end-to-end tests I suppose, to run them against the end product. &lt;/p&gt;

&lt;h4&gt;
  
  
  Running the Production Build
&lt;/h4&gt;

&lt;p&gt;To run the production build we have to options and each of them involve running a &lt;code&gt;Vite&lt;/code&gt; command, which is wrapped in an NPM tasks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;npm run build&lt;/code&gt; - this will generate the index.html and the other static assets. You can use this option if you already have a local web server set up.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run preview&lt;/code&gt; - this will generate a production build and run it using the built-in dev server, by default at &lt;code&gt;http://localhost:5000&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second option is clearly more straightforward and so let’s just run the &lt;code&gt;preview&lt;/code&gt; command and see what happens:&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="nv"&gt;$ &lt;/span&gt;npm run preview

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vue-bookstore@0.0.0 preview /Users/andrei/workspace/vue-bookstore
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vite preview

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Local: http://localhost:5000/
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Network: use &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; to expose

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Writing the Test Script
&lt;/h4&gt;

&lt;p&gt;Now that we have a production-ready build running, we can start writing the actual test in &lt;code&gt;test/e2e/homePageTest.js&lt;/code&gt;. We’ll start small, with just the below:&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="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="s1"&gt;Homepage End-to-end Test&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tests if homepage is loaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigateTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app .new-arrivals-panel&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;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app .new-arrivals-panel .col-md-6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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="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="s1"&gt;adds 2 volumes of "Rhinoceros and Other Plays" to cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;browser&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;.new-arrivals-panel .col-md-6:nth-child(2) button.add-to-cart&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;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;.new-arrivals-panel .col-md-6:nth-child(2) button.add-to-cart&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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.shopping-cart .badge&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;2&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;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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 test verifies if the New Arrivals panel is displayed in the page and that it contains all 4 entries which we’ve already seen.&lt;/p&gt;

&lt;h4&gt;
  
  
  Running the Test Script in Chrome
&lt;/h4&gt;

&lt;p&gt;To run this in Chrome, the command is very similar to the one for the component test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/e2e/homePageTest.js &lt;span class="nt"&gt;--env&lt;/span&gt; chrome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output will be:&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="o"&gt;[&lt;/span&gt;Homepage End-to-end Test] Test Suite
──────────────────────────────────────────────────────────────
ℹ Connected to ChromeDriver on port 9515 &lt;span class="o"&gt;(&lt;/span&gt;2454ms&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  Using: chrome &lt;span class="o"&gt;(&lt;/span&gt;97.0.4692.99&lt;span class="o"&gt;)&lt;/span&gt; on MAC OS X.


  Running tests the homepage:
──────────────────────────────────────────────────────────────
  ✔ Testing &lt;span class="k"&gt;if &lt;/span&gt;element &amp;lt;&lt;span class="c"&gt;#app .new-arrivals-panel&amp;gt; is visible (157ms)&lt;/span&gt;
  ✔ Expected elements &amp;lt;&lt;span class="c"&gt;#app .new-arrivals-panel .col-md-6&amp;gt; count to equal: "4" (18ms)&lt;/span&gt;

OK. 2 assertions passed. &lt;span class="o"&gt;(&lt;/span&gt;765ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Running the Test Script in Firefox
&lt;/h4&gt;

&lt;p&gt;If we’d want to also run our end-to-end tests in the Firefox browser, we only need to install the &lt;a href="https://github.com/mozilla/geckodriver" rel="noopener noreferrer"&gt;GeckoDriver&lt;/a&gt; (the Firefox specific implementation of the W3C WebDriver API). No other config is necessary to get it working, unless you’s like to customise it further.&lt;/p&gt;

&lt;p&gt;So let’s go ahead and install it from NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i geckodriver &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then run Nightwatch with 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;npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/e2e/homePageTest.js &lt;span class="nt"&gt;--env&lt;/span&gt; firefox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output:&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="o"&gt;[&lt;/span&gt;Homepage End-to-end Test] Test Suite
──────────────────────────────────────────────────────────────
ℹ Connected to GeckoDriver on port 4444 &lt;span class="o"&gt;(&lt;/span&gt;1737ms&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  Using: firefox &lt;span class="o"&gt;(&lt;/span&gt;96.0.2&lt;span class="o"&gt;)&lt;/span&gt; on MAC &lt;span class="o"&gt;(&lt;/span&gt;20.6.0&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;


  Running tests the homepage:
──────────────────────────────────────────────────────────────
  ✔ Testing &lt;span class="k"&gt;if &lt;/span&gt;element &amp;lt;&lt;span class="c"&gt;#app .new-arrivals-panel&amp;gt; is visible (54ms)&lt;/span&gt;
  ✔ Expected elements &amp;lt;&lt;span class="c"&gt;#app .new-arrivals-panel .col-md-6&amp;gt; count to equal: "4" (6ms)&lt;/span&gt;

OK. 2 assertions passed. &lt;span class="o"&gt;(&lt;/span&gt;612ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Running the Test Script in Safari
&lt;/h4&gt;

&lt;p&gt;If you’re using a Mac, then &lt;code&gt;safaridriver&lt;/code&gt; is probably already installed, depending on your Safari version. &lt;/p&gt;

&lt;p&gt;You can check with using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;safaridriver &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Usage: safaridriver &lt;span class="o"&gt;[&lt;/span&gt;options]
    &lt;span class="nt"&gt;-h&lt;/span&gt;, &lt;span class="nt"&gt;--help&lt;/span&gt;                Prints out this usage information.
    &lt;span class="nt"&gt;--version&lt;/span&gt;                 Prints out version information and exits.
    &lt;span class="nt"&gt;-p&lt;/span&gt;, &lt;span class="nt"&gt;--port&lt;/span&gt;                Port number the driver should use. If the server is already running, the port cannot be changed. If port 0 is specified, a default port will be used.
    &lt;span class="nt"&gt;--enable&lt;/span&gt;                  Applies configuration changes so that subsequent WebDriver                           sessions will run without further authentication.
    &lt;span class="nt"&gt;--diagnose&lt;/span&gt;                Causes safaridriver to log diagnostic information &lt;span class="k"&gt;for &lt;/span&gt;all sessions hosted by this instance. See the safaridriver&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt; man page &lt;span class="k"&gt;for &lt;/span&gt;more details about diagnostic logging.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running your first test in Safari, you just need to enable automation, with 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;safaridriver &lt;span class="nt"&gt;--enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then simply run the Nightwatch test with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/e2e/homePageTest.js &lt;span class="nt"&gt;--env&lt;/span&gt; safari
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running in Parallel in Multiple Browsers
&lt;/h3&gt;

&lt;p&gt;If you need to run your Nightwatch tests (either component or end-to-end) in more than one browser, you can also do so in more then one browser in parallel.&lt;/p&gt;

&lt;p&gt;Simply pass the browsers as a comma separated list (no spaces:&lt;/p&gt;

&lt;h4&gt;
  
  
  Running in Firefox+Chrome
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/e2e/homePageTest.js &lt;span class="nt"&gt;--env&lt;/span&gt; firefox,chrome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Running in Firefox+Chrome+Safari
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; npx nightwatch &lt;span class="nb"&gt;test&lt;/span&gt;/e2e/homePageTest.js &lt;span class="nt"&gt;--env&lt;/span&gt; firefox,chrome,safari
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nightwatch also supports running tests in parallel by dividing the total number of test script files over a configurable number of workers. But since we only have one file for now, we’ll skip this part. More on parallelism on the &lt;a href="https://nightwatchjs.org/guide/running-tests/parallel-running.html" rel="noopener noreferrer"&gt;Nightwatch docs&lt;/a&gt; website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 – Enabling Continuous Integration with Github Actions
&lt;/h2&gt;

&lt;p&gt;It looks like it’s time to wrap things up and put everything together. Before we can enable continuous deployment in Github Actions, we need to create the &lt;code&gt;test&lt;/code&gt; NPM task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the “npm test” Task
&lt;/h3&gt;

&lt;p&gt;Now we have both component testing and end-to-end testing in our example project. Of course, it is only at a minimum level so it doesn’t cover everything, but it’s a good start I would say. &lt;/p&gt;

&lt;p&gt;The easiest way to tell Nightwatch to run all the tests inside the test folder is to pass the folder as the second CLI argument. We’ll add that as a new NPM task called &lt;code&gt;test&lt;/code&gt; so let’s edit the &lt;code&gt;package.json&lt;/code&gt; and add the following, inside the “scripts” dictionary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nightwatch ./test"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run the NPM task and pass Nightwatch related CLI arguments like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--env&lt;/span&gt; chrome &lt;span class="nt"&gt;--headless&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll use &lt;code&gt;--headless&lt;/code&gt; mode in order to run the tests in Github Actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the Github Action workflow
&lt;/h3&gt;

&lt;p&gt;Finally we can add the Github Actions workflow so that our tests can run on every push and every pull request.&lt;/p&gt;

&lt;p&gt;Doing so it’s quite straightforward. We’ll use the Node.js template and add a few new steps in the list, for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starting the dev server in background&lt;/li&gt;
&lt;li&gt;building the project and starting the dev server in preview mode, also in background &lt;/li&gt;
&lt;li&gt;running both component and end-to-end tests in Chrome, in headless mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Creating the Github Actions workflow means to add a new file called &lt;strong&gt;node.js.yml&lt;/strong&gt; in the &lt;code&gt;.github/workflows&lt;/code&gt; folder which should look like below. Most of this is auto-generated when you navigate to the Actions section from your Github project and choose the Node.js template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Node.js CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;12.x&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;14.x&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use Node.js ${{ matrix.node-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Start vite dev server&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run dev &amp;amp;&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build the app&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Start vite dev server in preview&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run preview &amp;amp;&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Nightwatch tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. A new build will run for each new git push or whenever a new pull request is sent. The build will be ran in 2 separate environments, one for Node 12 and the other Node 14, as defined in the workflow definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Go From Here
&lt;/h2&gt;

&lt;p&gt;The project is available on Github at &lt;a href="https://github.com/beatfactor/middlemarch" rel="noopener noreferrer"&gt;https://github.com/beatfactor/middlemarch&lt;/a&gt; and all the code covered here and a bit more styling and imagery. It also contains the code for the shopping cart and a mock checkout page.&lt;/p&gt;

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

&lt;p&gt;You can get it running on your local machine with the usual steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/beatfactor/middlemarch
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to send pull requests or report issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Support
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Vue3, Vite, and Pinia**
&lt;/h4&gt;

&lt;p&gt;The Vue.js core team provides community support for Vue3, Vite, and Pinia on the following channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://discord.com/invite/aYVNktYeEB" rel="noopener noreferrer"&gt;VueLand&lt;/a&gt; chat server on Discord&lt;/li&gt;
&lt;li&gt;&lt;a href="https://forum.vuejs.org/" rel="noopener noreferrer"&gt;Vue Forum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vitejs/vite/discussions" rel="noopener noreferrer"&gt;Vite Discussions&lt;/a&gt; on Github&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vuejs/pinia/discussions" rel="noopener noreferrer"&gt;Pinia Discussions&lt;/a&gt; on Github&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Nightwatch.js&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;For support with everything Nightwatch testing related, we have the following channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nightwatchjs/nightwatch" rel="noopener noreferrer"&gt;Github Discussions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://labs.pineview.io/learn-how-to-build-test-and-deploy-a-single-page-app-with-vue-3-vite-and-pinia/#:~:text=Nightwatch.js%20chat%20server" rel="noopener noreferrer"&gt;Nightwatch.js chat server&lt;/a&gt; on Discord&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>vue</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Look at End-to-end Testing in Nightwatch v2.0</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Mon, 28 Jun 2021 10:31:21 +0000</pubDate>
      <link>https://dev.to/andreirusu_/a-look-at-end-to-end-testing-in-nightwatch-v2-0-3nl7</link>
      <guid>https://dev.to/andreirusu_/a-look-at-end-to-end-testing-in-nightwatch-v2-0-3nl7</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Nightwatch was published at the beginning of 2014 and was created as a complete and integrated framework which would enable engineers to write end-to-end tests quickly and without headaches. While writing it, we were guided by the belief that writing and running automated UI tests should be a straightforward and pleasant task, and should require as little configuration and maintenance as possible. &lt;/p&gt;

&lt;p&gt;The task of interacting with browser internals was already handled by the &lt;a href="https://selenium.dev"&gt;Selenium&lt;/a&gt; project and working with the Selenium server via an HTTP based api was a straightforward task. And so Nightwatch was born by bringing together various existing tools and techniques into one easy-to-use integrated solution.&lt;/p&gt;

&lt;p&gt;Seven years later, the open-source testing space for Node.js looks quite different. Several new frameworks appeared over the past few years, each of which is bringing their own set of capabilities to the table and their own interpretation over how automated testing should be done. &lt;/p&gt;

&lt;p&gt;Meanwhile, the Selenium &lt;a href="https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol"&gt;json-wire protocol&lt;/a&gt; has transition into the &lt;a href="https://www.w3.org/TR/webdriver/"&gt;W3C Webdriver&lt;/a&gt; standard which is now implemented by all major  browsers. As far as Nightwatch is concerned, the strategy hasn’t changed that much. In fact, I personally am even more confident to say that, as an open-source project, Nightwatch has now entered the next major phase in its development and maturity.&lt;/p&gt;

&lt;p&gt;The strategy for Nightwatch still remains that we should build the solution using tried-and-tested existing tools and techniques in the automation space instead of going our separate way. The Selenium project (which also includes Webdriver) has been around for more than a decade and has consistently refined and evolved how browser automation works, both on local development environments but also at scale, on large distributed cloud infrastructures. That’s why working in collaboration with the Selenium ecosystem makes me confident to say that Nightwatch will not only deliver on its promises but will even surpass expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nightcloud.io
&lt;/h2&gt;

&lt;p&gt;Another important update perhaps is that we have stopped developing our own cloud testing platform – &lt;a href="https://nightcloud.io"&gt;Nightcloud.io&lt;/a&gt;. We were supposed to launch an initial public beta last year, but we've pulled the plug on it I'm afraid. This is due to various reasons, but the main one I suppose is that we – the team at &lt;a href="https://pineview.io"&gt;Pineview.io&lt;/a&gt; – didn't see enough demand for it to justify the investment. We decided to focus on Nightwatch alone and try and make it the most sophisticated and user-friendly open-source testing framework out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nightwatch V2
&lt;/h2&gt;

&lt;p&gt;Which brings me to the most exciting part of this article I believe. Work is already under way for the next major update – Nightwatch v2, which should land in the public NPM channel by this fall. An alpha version will be available this August. &lt;/p&gt;

&lt;p&gt;We are completely reworking the transport layer to use the official &lt;a href="https://www.npmjs.com/package/selenium-webdriver"&gt;Selenium Webdriver&lt;/a&gt; bindings for Node.js, which means more seamless browser integration, better and more reliable DOM element handling, and overall more stable and faster tests. Nightwatch v1.x will still be supported and we’ll issue regular patch updates for critical issues. We’ll also try to make the upgrade from Nightwatch v1.5 or higher to v2 as smooth as possible and we’ll try to not introduce any major breaking changes.&lt;/p&gt;

&lt;p&gt;Here’s some of the new features in v2 which you might find attractive:&lt;/p&gt;

&lt;h3&gt;
  
  
  • Support for Actions API
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html"&gt;Actions api&lt;/a&gt; provides a more reliable method for generating complex user gestures and will be a built-in feature in Nightwatch (via the existing &lt;code&gt;.perform()&lt;/code&gt; command).&lt;/p&gt;

&lt;h3&gt;
  
  
  • Expanded automatic command retries
&lt;/h3&gt;

&lt;p&gt;In the current version, Nightwatch only supports retrying failed element retrieval operations, but in v2 it will also retry failed element commands (such as when click encounters errors like &lt;code&gt;element click intercepted&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  • New relative element locators
&lt;/h3&gt;

&lt;p&gt;The new Selenium 4 &lt;a href="https://www.selenium.dev/documentation/en/webdriver/locating_elements/#relative-locators"&gt;relative locators&lt;/a&gt; will be available by default in Nightwatch as well as improved existing locators via the Selenium &lt;a href="https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_By.html"&gt;By()&lt;/a&gt; api. &lt;/p&gt;

&lt;h3&gt;
  
  
  • Built-in support for working with file uploads
&lt;/h3&gt;

&lt;p&gt;Currently uploading files with Nightwatch can be cumbersome, but in v2 we’ll add a built-in command which works with the &lt;a href="https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_FileDetector.html"&gt;FileDetector api&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  • Support for extended Capabilities objects
&lt;/h3&gt;

&lt;p&gt;The primary way of defining capabilities will still be as part of the &lt;code&gt;nightwatch.conf.js&lt;/code&gt; config file, but in v2, all capabilities objects created with the Selenium &lt;a href="https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_Capabilities.html"&gt;Capabilities&lt;/a&gt; api will be also be supported by default. &lt;/p&gt;

&lt;h3&gt;
  
  
  • Setting network conditions in Chrome
&lt;/h3&gt;

&lt;p&gt;Manipulating network conditions is a regularly needed feature when working with browser automation and we’ll be able to support it in v2 for Chrome and Edge initially.&lt;/p&gt;

&lt;h3&gt;
  
  
  • Support for working with Devtools Protocol
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://chromedevtools.github.io/devtools-protocol/"&gt;Chrome DevTools Protocol&lt;/a&gt; allows for tools to instrument, inspect, debug and profile Chromium-based browsers, such as Chrome and Edge, and we’ll add this capability in Nightwatch v2. &lt;/p&gt;

&lt;h3&gt;
  
  
  • Ready for Webdriver BiDi
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://w3c.github.io/webdriver-bidi/"&gt;BiDirectional WebDriver Protocol&lt;/a&gt; is by far one of the most exciting recent developments in the Selenium project and it likely to deliver improvements in speed and stability that far outweigh expectations. As soon as it is available, it will become part of Nightwatch as well. You can follow the development on the &lt;a href="https://www.selenium.dev/documentation/en/webdriver/bidi_apis/"&gt;Selenium website&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  • Various new command APIs
&lt;/h3&gt;

&lt;p&gt;Nightwatch v2 will also bring a new &lt;code&gt;.ensure&lt;/code&gt; assert api which will mirror the &lt;a href="https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/until.html"&gt;Selenium until&lt;/a&gt; apis, global &lt;code&gt;element()&lt;/code&gt;, &lt;code&gt;expect()&lt;/code&gt;, &lt;code&gt;by()&lt;/code&gt;, and &lt;code&gt;browser&lt;/code&gt; objects which will enhance the experience of writing tests and provide more flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  • Test runner improvements
&lt;/h3&gt;

&lt;p&gt;Nightwatch v2 will also bring some improvements to the test runner, like ability to re-run only failed tests and improved support for parallel test execution.&lt;/p&gt;

&lt;p&gt;The list above is not exhaustive and it might change until the initial public release of version 2. You can also review the updates published in &lt;a href="https://github.com/nightwatchjs/nightwatch/releases/tag/v1.7.3"&gt;Nightwatch v1.7&lt;/a&gt; in case you may have missed anything. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tell Us Your Opinion
&lt;/h2&gt;

&lt;p&gt;We are also considering other new features, APIs, and integration with other tools like Appium or Playwright. It would be very helpful if you’d take the time to fill in &lt;a href="https://forms.gle/ahi2bTk7Kz4og8VK9"&gt;this short survey&lt;/a&gt; and tell us about features you are using or would like to see in the new version. Please do share it with your team.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Please make sure to &lt;a href="https://github.com/nightwatchjs/nightwatch"&gt;star the project&lt;/a&gt; on Github and also to follow us on Twitter; we're at &lt;a href="https://twitter.com/nightwatchjs"&gt;@nightwatchjs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The Illusion of Innovation in Web Development</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Thu, 06 Aug 2020 21:23:33 +0000</pubDate>
      <link>https://dev.to/andreirusu_/the-illusion-of-innovation-in-web-development-c5h</link>
      <guid>https://dev.to/andreirusu_/the-illusion-of-innovation-in-web-development-c5h</guid>
      <description>&lt;p&gt;Today I somehow ended up on Google's web.dev platform, which I'm assuming it's rather new. There is of course the possibility that I have been–or still am–living under a rock, when it comes to new web technologies. Something caught my attention–an &lt;a href="https://web.dev/app-shell-ux-with-service-workers/"&gt;article&lt;/a&gt; about using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API"&gt;Service Workers&lt;/a&gt; and &lt;a href="https://streams.spec.whatwg.org/#intro"&gt;streams&lt;/a&gt; to deliver partial updates to a website: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The key element of this solution is the usage of streams, which enables incremental creations and updates of data sources."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's actually using the DEV.to website as a use-case, based on Ben Halpern's &lt;a href="https://dev.to/devteam/instant-webpages-and-terabytes-of-data-savings-through-the-magic-of-service-workers-1mkc"&gt;article&lt;/a&gt; here on DEV, from last year. In his piece, Ben is enthusiastically praising the "magic" of service workers to deliver on saving “terabytes of data” in network transfers. According to him, this approach enables the team at DEV to “ship many fewer bytes while also controlling the user experience with more precision.” To see the results–which are indeed very impressive–you only have to reload the page to his article, or this very article for that matter. &lt;/p&gt;

&lt;p&gt;This architecture is known as the &lt;a href="https://developers.google.com/web/fundamentals/architecture/app-shell"&gt;App Shell Model&lt;/a&gt; and it seems to be growing in popularity lately. It might in fact be the most innovative development in frontend web space since the dawn of AJAX and JSON. The basic idea is to deliver only a very minimal page structure in the initial request and then gradually deliver the rest via fragments or partials, while leveraging caching. So is this the most paradigm shifting web architecture since a decade ago? It does certainly have the potential. I'm a little skeptical regarding the use of the term "magic” though, as is the case in Ben Halpern’s article. &lt;/p&gt;

&lt;h2&gt;
  
  
  On Magic
&lt;/h2&gt;

&lt;p&gt;Magic is not innovation, it's the opposite of it. Without getting too much into the metaphysics or the occult, I think we can agree that traditional magic works by creating illusions. And just in case we do want to get into metaphysics, even if for a little bit, there's a great 2003 &lt;a href="https://www.ted.com/talks/dan_dennett_the_illusion_of_consciousness"&gt;Ted talk&lt;/a&gt; by philosopher and cognitive scientist Dan Dennett, called "The illusion of consciousness", which he starts by humorously explaining the difference between magic and real magic (spoiler: there isn't one): &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Real magic, in other words, refers to the magic that is not real, while the magic that it is real, that can actually be done, is not real magic."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Confused? I know I am. But don't worry, it's only because consciousness itself, or the mind, the self, however you want to call it, "is a bag of tricks", according to Dennett. In other words, it's an illusion. When something is truthful it's only because it appears to be so to our consciousness, and however truthful something appears to us depends on how convinced we &lt;em&gt;feel&lt;/em&gt; about it.&lt;/p&gt;

&lt;p&gt;But enough about magic. Let's not ruin it, shall we? Let's keep the magic alive. Back to service workers. My main concern is that saying it's "magic" will make it difficult for this technology to be properly understood. A certain technological breakthrough can be an actual innovation only if it's widely adopted. And for service workers to become widely adopted, they have to be properly understood by everybody, not just the most advanced and experienced engineers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Slickness is Tedious
&lt;/h1&gt;

&lt;p&gt;I remember being first introduced to AJAX (&lt;em&gt;Asynchronous JavaScript And XML&lt;/em&gt;) in 2006 and even as I was a junior developer (by experience) I recall having clearly understood what it does–updating the page by doing HTTP calls in the background, without reloading the page. We call this a single-page application nowadays (or SPA–not an acronym which I'm very fond of, personally). And thinking at the past decade, I can't say that I'm convinced of anything being a major innovation in how websites and web applications are built and how they work. &lt;/p&gt;

&lt;p&gt;There are of course several high profile Javascript frameworks, the language itself has been tremendously improved in terms of readability and maintenance, and several new html5 apis have been added, but to me it seems that the actual web UI looks pretty much the same as it did in the jQuery days, by which I mean about a decade ago. And if there are any actual innovations, those are mainly the domain of aesthetics, i.e. graphical design, such as SVG or WebFonts, and not performance or interaction.&lt;/p&gt;

&lt;p&gt;While the aesthetics may or may not be important, as the visual impact can vary from one person to another, the performance impact is certainly the most important one to consider. When measuring the visual impact, there's a certain threshold above which the user interface becomes stable and subsequent updates are more or less redundant. There's a certain point where the application is slick enough and any amount of additional slickness is irrelevant, and it might even be tedious.&lt;/p&gt;

&lt;p&gt;On the other hand, a performance update that can "magically" make the app load much master can have a dramatic effect for everyone who's using it. Improvements in performance can have near universal benefits, because it saves us time–we spend less time while waiting for an event to happen in the application (such as a page or an image to load). A performance benefit is like time regained, because the way time is perceived by our consciousness is shared across all of us, presumably.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4w70CGop--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developers.google.com/web/updates/2018/05/images/beyond-spa/sw-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4w70CGop--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developers.google.com/web/updates/2018/05/images/beyond-spa/sw-diagram.png" alt=""&gt;&lt;/a&gt;Service worker diagram from &lt;a href="https://developers.google.com/web/updates/2018/05/beyond-spa"&gt;Beyond SPAs: alternative architectures for your PWA&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Not Another Bag of Tricks
&lt;/h1&gt;

&lt;p&gt;The “App Shell Model” architecture, as Ben Halpern has shown, sure seems to be &lt;em&gt;the&lt;/em&gt; innovation which delivers on the performance gain. But the technology to implement it has been existing for a good while. As Ben also points out, streams have been around as long as the web itself, and also a server-push mechanism has been available in all major browsers for longer than a decade ago, namely the EventSource API, or server sent events. I have myself done a &lt;a href="https://www.slideshare.net/beatfactor/sse-23276287"&gt;presentation&lt;/a&gt; about it just seven years ago.&lt;/p&gt;

&lt;p&gt;In fact, the initial content-type of the events being sent through this API was &lt;em&gt;application/x-dom-event-stream&lt;/em&gt; and have been later changed into the more generic &lt;em&gt;text/event-stream&lt;/em&gt; type, according to &lt;a href="https://dev.opera.com/blog/event-streaming-to-web-browsers/"&gt;this&lt;/a&gt;, so presumably the EventSource API has been designed for this purpose, as a mechanism which would accommodate streaming updates to the DOM from the server. Bearing in mind also that &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment"&gt;document fragments&lt;/a&gt; have also been existing since the dawn of time, you got yourself a complete server-push-to-dom-fragment architecture. &lt;/p&gt;

&lt;p&gt;These days however, what counts as innovation is mostly determined by the big tech “innovators” in Silicon Valley and the innovation itself is usually permitted to happen only if it furthers an agenda of economic growth and corporate prosperity. Even if it does happen on the outside of the big tech corporations–and remember that the world needs massive corporations just as it needs small businesses, as Zuckerberg recently pointed out–the innovation is absorbed and marketed properly to maximise the potential of value delivery. But what does it matter, innovation is innovation regardless of where it comes from, when it does make it. Whether this particular innovation–the “App Shell Model” architecture (we might want to do something about this naming)–is the next AJAX remains to be determined, of course. Personally, I am quite enthusiastic at the prospect of yet again having an actual innovation in website and application building and not just another bag of tricks.&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
&lt;span&gt;Main photo by &lt;a href="https://unsplash.com/@moren"&gt;moren hsu&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/4887683/fdp"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

</description>
      <category>meta</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>webperf</category>
    </item>
    <item>
      <title>Digital Ecology</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Sat, 18 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/andreirusu_/digital-ecology-l6a</link>
      <guid>https://dev.to/andreirusu_/digital-ecology-l6a</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B4G5XCVz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ApFIp_okCTqhn9TGUoEElkQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B4G5XCVz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ApFIp_okCTqhn9TGUoEElkQ.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@popnzebra"&gt;Pop &amp;amp; Zebra&lt;/a&gt; on Unsplash&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The end is in the beginning and yet you go on."&lt;/em&gt; — Samuel Beckett&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Almost exactly one year ago I found myself sitting in a meeting room together with four gentlemen. I was interviewing for a scientific programmer job at a small software consultancy, at that time located somewhere outside Oslo. It was a pleasant sort of affair, though at one specific point during the discussion, the atmosphere in the room took an expected turn.&lt;/p&gt;

&lt;p&gt;"He’s one of those environmentalists" — the CEO was probably thinking, by the curious expression on his face and slight change of tone in his speech. His attitude appeared to become more defensive and he also seemed surprised that I’d be preoccupied with matters concerning the climate. After all, this is a sought-after software engineer position where one would be working with several cloud technologies, in a team of geo-physicists and data scientists. And the company was also a spin-off of a reputable software research institute in Norway.&lt;/p&gt;

&lt;p&gt;The advertisement said the company is developing solutions for the energy sector. I don’t know exactly why, but I assumed “renewable” energy. But the story that was presented to me during the interview was slightly different from what I imagined. I’ve been presented with a sophisticated desktop application which was used in the development of seismic modelling of the ocean floor, for the purpose of discovering and exploration of oil reserves. One of the main customers was the biggest Norwegian producer of oil &amp;amp; gas, Equinor.&lt;/p&gt;

&lt;p&gt;“But the world is transitioning to renewable energy sources,’’ I said after they finished the presentation, with what must have been an unexpected interruption. And even if the atmosphere has started to become considerably awkward, I kept on and, addressing the whole group, I asked if they had seen the latest &lt;a href="https://www.ipcc.ch/sr15/"&gt;IPCC report on global warming&lt;/a&gt;. At that time, the CEO didn’t seem familiar with the report, but one of the scientists was and mentioned something to him. Eventually the CEO made a remark saying that of course they agreed with the need to transition to renewable energy sources but that needs to be addressed from an economic point of view as well. The rest of the group didn’t seem to disagree with the narrative.&lt;/p&gt;

&lt;p&gt;Equinor has published last year a press release celebrating Norway’s third largest oil field beginning production, and branding it “a technological triumph and a milestone for the Norwegian oil industry”.&lt;/p&gt;

&lt;p&gt;They also seem to acknowledge the severity of the climate crisis and the importance of cutting down on emissions. “It’s 2019 and climate strikes are taking place the world over. So why are we so proud of our new oil field?”, says on the &lt;a href="https://www.equinor.com/en/what-we-do/johan-sverdrup.html"&gt;promotional page&lt;/a&gt; of their newly opened oil field.&lt;/p&gt;

&lt;p&gt;Why indeed. Equinor is no doubt trying to have their cake and eat it, as they say in Britain. They are reassuring shareholders and doing everything in their capacity to appear supportive of the Paris accord, while still producing more oil.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Age of Perpetual Crisis
&lt;/h3&gt;

&lt;p&gt;“I can feel the discomfort in your seat”, the lyrics of a popular song goes. 2019 was a year when the climate crisis has reached the forefront of the public imagination, and any stories about it now carry a significantly higher degree of discomfort. Headlines are telling us, in a rather alarmed tone, that we are living in the age of perpetual crisis — environmental, social, political. “It’s a Jungle out there”, as another popular tune would say.&lt;/p&gt;

&lt;p&gt;I’ve considered writing something on climate from a practical point of view, to examine how the tech industry might be able to contribute with direct solutions. Such an article would probably have been something like a call to action — let’s get enough people interested, raise awareness of the environmental crisis, and then start working on cleantech solutions. If we can deploy some capital on green start-up accelerators, maybe we can make a significant contribution to solving the biggest crisis that humanity is facing at the moment. And hopefully someone will turn off the oil pipes.&lt;/p&gt;

&lt;p&gt;I did try to write such a piece. I started collecting stories related to climate and the environment from the tech industry. Noticeably, there aren’t so many such stories. In the tech industry, the collective preoccupation with climate change or the environment is still severely limited. Anything that has to do with the climate or the natural world appears merely as an after-thought, and gets dismissed almost instantly as something which happens somewhere else.&lt;/p&gt;

&lt;p&gt;As my research was expanding, my discomfort was mounting. My original idea to write a piece on climate from a software developer point of view looked increasingly like a superfluous and banal enterprise. I’ve come to the conclusion that initiatives like &lt;a href="https://www.blog.google/outreach-initiatives/sustainability/100-percent-renewable-energy-second-year-row/"&gt;carbon-neutral data centers&lt;/a&gt; have ultimately the effect of a drop in a bucket, compared to what the response to the climate crisis should be. Instead I decided to investigate why is there such an enormous lack of urgency and disconnect between where the tech industry is at the moment and where it should collectively be, and if it is even conceivable for the tech industry to be focused on solutions for the climate crisis?&lt;/p&gt;

&lt;p&gt;Speaking of collective action, the open source funding platform &lt;a href="https://opencollective.com/"&gt;OpenCollective&lt;/a&gt; recently published an &lt;a href="https://blog.opencollective.com/digital-climate-strike/"&gt;article&lt;/a&gt; on their blog in which they have sounded the alarm regarding our current predicament and they have announced their participation in the &lt;a href="https://digital.globalclimatestrike.net/"&gt;Global Digital Climate Strike&lt;/a&gt;, urging others to do the same. The movement was initiated by the international climate campaign group &lt;a href="http://350.org"&gt;350.org&lt;/a&gt;. The action consists of temporarily shutting down your website or service, in part or completely, in protest against the lack of policies to tackle the climate crisis. 350.org is also behind the &lt;a href="https://globalclimatestrike.net/"&gt;global climate strikes&lt;/a&gt; and is backing Greta Thunberg’s &lt;a href="https://fridaysforfuture.org/"&gt;Fridays For Future&lt;/a&gt; school strikes movement, which have proved the most effective and galvanizing climate movements in recent years, if not decades.&lt;/p&gt;

&lt;h3&gt;
  
  
  Climate Change Can’t Be Solved With Enthusiasm
&lt;/h3&gt;

&lt;p&gt;Alright, but why is this our problem? We’re in the software development business, and our primary concern is to build and ship software. How is this our responsibility? Insisting on this argument makes the entire tech industry look like the &lt;a href="https://en.wikipedia.org/wiki/Musicians_of_the_RMS_Titanic"&gt;musicians&lt;/a&gt; on board of the sinking Titanic ship, playing to the tune of the inevitability of their peril. But the tech industry, being mainly concerned at the moment with things like AI, ML, Blockchain, or crypto currencies, hasn’t even realised the terrifying course we are on as a planet.&lt;/p&gt;

&lt;p&gt;Instead, it is perfectly in tune with the neoliberal economic model which demands exponential growth through free-market capitalism, without the slightest concern for the climate and the effects that digital technology has on the environment. Even more so, when it comes to technologies like Blockchain, everyone is in love with its potential to deliver on the libertarian dream of decentralisation and democratization of institutions, with even the UN &lt;a href="https://unfccc.int/news/un-supports-blockchain-technology-for-climate-action"&gt;endorsing it&lt;/a&gt;, and for climate action no less.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nwGjJLzg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AZYAWXj-D0dQE8IYDQ6xQGA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nwGjJLzg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AZYAWXj-D0dQE8IYDQ6xQGA.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@simplicity"&gt;Marija Zarik&lt;/a&gt; on Unsplash&lt;/p&gt;

&lt;p&gt;Still, one tech founder &lt;a href="https://twitter.com/jjacobs22/status/1210290742222114820"&gt;tells me&lt;/a&gt; on Twitter that there is “growing concern” in the tech industry, which might suggest there is some hope that maybe climate change and the environment will stop being the annoying buzz-killers in the background.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Climate change is the new crypto”,&lt;/em&gt; another tech investor &lt;a href="https://twitter.com/bryce/status/1191712870419124224"&gt;wrote&lt;/a&gt; on Twitter last November, suggesting that many of the “smartest minds in tech” are shifting their enthusiasm to working on solutions for the climate crisis. The appetite for working on climate solutions in the tech industry does appear to be growing at the moment, although no major government seems capable of entertaining the idea that fossil fuels must stay in the ground.&lt;/p&gt;

&lt;p&gt;Okay, so isn’t this good news, that there is some preoccupation in the tech industry? Not necessarily, no. For one, almost all efforts in the tech world seems to focus on legitimising its operations, either digital or logistical, by cleaning out its act, for instance with Google’s carbon neutral data centers. In the specific crypto sphere, there are also detailed reports, such as &lt;a href="https://medium.com/coinmonks/the-reports-of-bitcoin-environmental-damage-are-garbage-5a93d32c2d7"&gt;this one&lt;/a&gt;, which goes to great lengths to claim that the crypto mining operations use around 85% renewable energy.&lt;/p&gt;

&lt;p&gt;Climate change is not an issue to be solved with enthusiasm. You can’t simply fix global warming by launching &lt;a href="https://www.cnbc.com/2019/11/05/google-employees-protest-over-climate-change-in-letter-to-cfo.html"&gt;green start-up&lt;/a&gt; accelerators and &lt;a href="https://www.ft.com/content/21009e1c-d8c9-11e9-8f9b-77216ebe1f17"&gt;investing&lt;/a&gt; in “disruptive” new technologies to lower emissions, despite what Bill Gates would have you believe. It’s not enough to &lt;a href="https://devopsdays.org/events/2019-oslo/program/marta-paciorkowska/"&gt;make our applications greener&lt;/a&gt; and our data centers carbon neutral and then carry on with business as usual. Such initiatives not only have little to no actual effect, but they may obscure the reality, potentially giving the impression that things are going well and action is being done. And the harsh reality is that the tech industry still works hand-in-hand with the &lt;a href="https://www.theguardian.com/environment/2019/aug/01/fossil-fuel-subsidy-cash-pay-green-energy-transition"&gt;already heavily subsidised&lt;/a&gt; fossil fuel industry in enthusiastically supplying digital technology and infrastructure, such as cloud data platforms.&lt;/p&gt;

&lt;p&gt;For execs of big tech companies, taking a strong position in fighting the climate crisis is nothing more than a ridiculous fantasy. To Amazon, for instance, the idea that they should take any meaningful action sounds so obscene that they have to resort to &lt;a href="https://www.washingtonpost.com/technology/2020/01/02/amazon-threatens-fire-outspoken-employee-critics-its-environmental-policies/"&gt;coercive measures&lt;/a&gt; against their employees who organize and demand it. Amazon isn’t the only major tech company facing calls for stronger climate action.&lt;/p&gt;

&lt;p&gt;Google is also facing mounting pressure, with more than 2,000 employees having signed a &lt;a href="https://medium.com/@googworkersac/google-workers-are-striking-for-climate-sept-20-7eba2100b621"&gt;pledge&lt;/a&gt; supporting climate action and a separate &lt;a href="https://medium.com/@googworkersac/ruth-porat-497bbb841b52"&gt;open letter&lt;/a&gt; which calls on the tech giant to commit to and release a company-wide climate plan. There is also an industry wide &lt;a href="https://techworkerscoalition.org/climate-strike/"&gt;tech workers coalition&lt;/a&gt;, albeit only in its infancy, but acknowledging “tech’s dirty role in climate change” and voicing bold demands. In addition, there is a global community &lt;a href="https://climateaction.tech/"&gt;ClimateAction.tech&lt;/a&gt; where tech professionals can connect with one another on matters concerning climate change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsibility Is Not Predicated By Guilt
&lt;/h3&gt;

&lt;p&gt;Perhaps one aspect can offer the best explanation to why individuals are being reluctant to demand stronger climate action. It is quite uncontroversial to say that people all over the world are responsible for global warming. If only we could have done a bit more recycling, if only we would have changed the petrol engine car to a Tesla, maybe we wouldn’t have ended up in this mess.&lt;/p&gt;

&lt;p&gt;“Anyone can make a change”, isn’t this what we’re told? Sure, anyone has the power to turn off the oil pipes. Each of us sits on the board of multinational corporations and can decide how much rainforest is going to be wiped out to grow palm oil so we can enjoy a chocolate spread for breakfast. Don’t we all have millions to spend on financing think-tanks so we can lobby policy makers? Yes, anyone can make a real change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--la5_FnBc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A7NYXTWJXvjbGfE8NlEr3IQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--la5_FnBc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A7NYXTWJXvjbGfE8NlEr3IQ.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@onesaveaday?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;OneSave/Day&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I write this, Australia is battered by the most severe wildfires ever witnessed in its history, and take for instance Richard Flanagan’s &lt;a href="https://www.theguardian.com/commentisfree/2019/dec/12/what-albanese-could-have-said-we-lied-australian-coalmines-have-no-future"&gt;December column&lt;/a&gt; for The Guardian, who’s one of Australia’s most prominent novelists and also an outspoken environmental campaigner. Referring to the climate crisis, he makes the statement that “human beings all over the world are responsible [for causing it]”. This is a fundamental problem in the way in which we talk about the climate crisis. Humans as a species have caused global warming, but you personally did not cause it, even if you read this article on an iPhone.&lt;/p&gt;

&lt;p&gt;Nor had I caused global warming, just because I am writing this on a laptop. Individuals aren’t guilty for mass extinction, which is what global warming leads to, but guilt is being scaled down to individuals. We are however, as a species, as a whole, responsible to fix it, but responsibility is not predicated by guilt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Being Ecological In The Anthropocene
&lt;/h3&gt;

&lt;p&gt;As an individual, you can’t do anything to stop climate change, however you may be prepared to change. We need a radical new attitude to the climate crisis, ecology, and the environment, which should begin with not worrying about our own individual carbon footprint. In the current free-market, global economic system any individual efforts of minimising it are practically worthless.&lt;/p&gt;

&lt;p&gt;In addition, our current anthropocentric attitude towards ecology, especially in the tech world, is incompatible with any kind of effective climate action. The theorist Timothy Morton, who’s been dubbed “the philosopher prophet of the Anthropocene” by &lt;a href="https://www.theguardian.com/world/2017/jun/15/timothy-morton-anthropocene-philosopher"&gt;The Guardian&lt;/a&gt;, makes the case that thinking in an ecological way requires that we let go of our current idea of nature. In his last book &lt;a href="https://mitpress.mit.edu/books/being-ecological"&gt;Being Ecological&lt;/a&gt;, Morton, who adheres to a school of philosophical thought known as &lt;em&gt;Object Oriented Ontology (OOO)&lt;/em&gt;, argues that our pastoral view of nature, our association of it with rolling hills, green meadows, birdsong, and so on, are very problematic and may even prevent us from contemplating any effective action.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;According to &lt;a href="https://twitter.com/the_eco_thought"&gt;Timothy Morton&lt;/a&gt;, nature isn’t &lt;em&gt;“actual trees and bunny rabbits, it’s a concept, an interpretation”.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Being ecological doesn’t mean that we must become aware of the environment and the natural world, it means that we discard the idea that there is a separation in the first place. There is no separation between an iPhone and a bunny rabbit, or a slug for that matter. Everything is interconnected, and it’s not the bunny rabbits that are causing mass extinction. They can’t turn off the oil pipes by themselves. If you read this and think that it sounds ludicrous, consider that for thousands of years this ecological way of co-existence with the environment, known as &lt;a href="https://www.researchgate.net/publication/242186767_Kincentric_Ecology_Indigenous_Perceptions_of_the_HumanNature_Relationship"&gt;kincentric ecology&lt;/a&gt; has been common to most indigenous peoples around the world. Something which the modern Eurocentric idea of progress through industrial capitalism has instructed us to dismiss in an instant.&lt;/p&gt;

&lt;p&gt;It is for all accounts and purposes unrealistic to expect strong action on climate from big tech companies, who will most likely take any ecological idea and laugh it out as preposterous hippie-dippie nonsense. However tech workers are very far from having explored all avenues that could lead to collective, effective action, especially when you consider the fact that most of the software which powers the world’s digital infrastructure is by now open-source. Climate strikes, along with action by the activist group &lt;a href="https://rebellion.earth/"&gt;Extinction Rebellion&lt;/a&gt;, have been proven the most effective and galvanising forms of climate action in the past year. Indeed, as Oscar Wilde wrote in 1891, “it is through disobedience that progress has been made, through disobedience and through rebellion.”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://hackernoon.com/digital-ecology-anyone-can-make-a-change-xbfq3201"&gt;&lt;em&gt;https://hackernoon.com&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on January 18, 2020.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecology</category>
      <category>productivity</category>
      <category>motivation</category>
      <category>climateaction</category>
    </item>
    <item>
      <title>I am not a Hacker</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Tue, 04 Jun 2019 14:23:23 +0000</pubDate>
      <link>https://dev.to/andreirusu_/i-am-not-a-hacker-ch0</link>
      <guid>https://dev.to/andreirusu_/i-am-not-a-hacker-ch0</guid>
      <description>&lt;p&gt;The term hacker seems to be enjoying great popularity, with websites like HackerNews, HackerNoon, HackerRank, recruiting events like HackerX or the all-too-familiar and ever prevalent "hackathons". It's a hacking bonanza.&lt;/p&gt;

&lt;p&gt;I have never referred to myself a hacker and every time a go to a HackerX event or a hackathon I feel uncomfortable and out of place.&lt;/p&gt;

&lt;p&gt;When are we going to be past this hacking tomfoolery, hacking "culture", hackers, hackathons?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Music for coding – what do you listen to?</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Wed, 29 May 2019 11:31:33 +0000</pubDate>
      <link>https://dev.to/andreirusu_/music-for-coding-what-do-you-listen-to-4mne</link>
      <guid>https://dev.to/andreirusu_/music-for-coding-what-do-you-listen-to-4mne</guid>
      <description>&lt;p&gt;I'm almost always listening to music while I am coding, and especially when I am at home, where I don’t need to wear my headphones. I listen to a lot of different music and usually try to listen to new music as well, something that it’s unfamiliar or that I haven’t heard before. &lt;/p&gt;

&lt;p&gt;Some studies show that listening to music while working "&lt;a href="https://medicalxpress.com/news/2019-05-music-productive.html"&gt;can make us more productive&lt;/a&gt;". However, &lt;a href="https://medicalxpress.com/news/2019-02-music-significantly-impairs-creativity.html"&gt;this study&lt;/a&gt;, suggests that listening to music may "disrupt creative performance in insight problem solving".&lt;/p&gt;

&lt;p&gt;If I am working at home I can't stand being in a quiet room, so I need to listen to music. If I'm in an office I usually listen to something on my headphones, but not always. Sometimes I just listen to the noise of the room if it's not too disruptive.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/IJ8q0FYfb-Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The first instance of background music appeared, as far as I can tell, in France in 1917 when composer Eric Satie coined the term &lt;a href="https://en.wikipedia.org/wiki/Furniture_music"&gt;Furniture Music&lt;/a&gt;. Not a very flattering term, but I think you will find some of his music, such as the &lt;a href="https://www.youtube.com/watch?v=IJ8q0FYfb-Q"&gt;&lt;em&gt;Gymnopédie No. 1&lt;/em&gt;&lt;/a&gt;, very well suited as background music while working. And this particular composition is almost always found in those several hours long playlists of classical music for concentration and mindful listening that are everywhere on Spotify, Youtube etc., together with Chopin's Nocturnes, Schubert sonatas and the list goes on. &lt;/p&gt;

&lt;p&gt;Is Classical Music then best suited for this job? Perhaps. At least, some of it. I think if you listen to, for example &lt;a href="https://www.youtube.com/watch?v=wClwaBuFOJA"&gt;&lt;em&gt;Ionisation&lt;/em&gt;&lt;/a&gt;, a piece written in 1931 by &lt;a href="https://en.wikipedia.org/wiki/Edgard_Var%C3%A8se"&gt;Edgard Varèse&lt;/a&gt;, you will find that this might not work very as background music, at least in the same way that Satie’s music does. I'm not even going to mention composers from the &lt;a href="https://en.wikipedia.org/wiki/Darmstadt_School"&gt;Darmstadt School&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bbc.co.uk/sounds/play/m0001hp4"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HL5naHzi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ichef.bbci.co.uk/images/ic/640x360/p06szby1.jpg" alt="Hermione Hoby on Steve Reich"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Is it music Pop music with vocals then? Or maybe Jazz? Perhaps Electronic dance music? I also find that music with repetitive structures works very well, which is at the core of a current of contemporary classical music known as &lt;a href="https://en.wikipedia.org/wiki/Minimal_music"&gt;Minimalism&lt;/a&gt; and in &lt;a href="https://www.bbc.co.uk/programmes/m0001hp4"&gt;this Essay&lt;/a&gt; for BBC Radio 3 New York based novelist &lt;a href="https://twitter.com/hermionehoby"&gt;Hermione Hoby&lt;/a&gt; reveals how she always listens to Steve Reich's &lt;a href="https://www.youtube.com/watch?v=ApnbymNz9dE"&gt;Music For 18 Musicians&lt;/a&gt; while writing. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ApnbymNz9dE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This might sound somewhat pretentious, but isn't writing code also some form of writing? We are all creators with stories to tell, are we not? What particular piece of music do you rely on to telling those stories? &lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/photos/ciO8yG5BDso?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Corey Silva on Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>music</category>
    </item>
    <item>
      <title>The Missing Introduction to Kafka</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Thu, 11 Apr 2019 13:21:00 +0000</pubDate>
      <link>https://dev.to/andreirusu_/the-missing-introduction-to-kafka-ih</link>
      <guid>https://dev.to/andreirusu_/the-missing-introduction-to-kafka-ih</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A9CYuSvAK4f19wR3T" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A9CYuSvAK4f19wR3T"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@sakethgaruda?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Saketh Garuda&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have been fascinated by the open-source Apache Kafka service platform since I first heard about it some years ago. In part for the goal it has set out to achieve — to make microservices talk to each other, but also because of its name.&lt;/p&gt;

&lt;p&gt;Why is it named Kafka? This is indeed something I find curious. There is a certain sense of intrigue when a software library or platform goes a bit further than the norm and picks a more unusual name. In this case the name &lt;strong&gt;Kafka&lt;/strong&gt; goes well beyond the trivialities of daily existence and into a surreal literary realm.&lt;/p&gt;

&lt;p&gt;I wholeheartedly welcome any sort of connection to anything foreign to software and technology, such as the literary world of &lt;a href="https://en.wikipedia.org/wiki/Franz_Kafka" rel="noopener noreferrer"&gt;Franz Kafka&lt;/a&gt; in the case of Apache Kafka. Working as a software engineer for more than a decade, I sometimes begin to look at software and technology as a sort of static, dehumanized space of data and algorithms where nothing much happens and where the banal and the mundane thrives. And I don’t mean dehumanizing in a dystopian technological singularity sort of way with artificial super-intelligence controlling the world. I mean it in a purely modern way, as someone who earns a living in the software space which is often void of human interactions.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://en.wikipedia.org/wiki/Apache_Kafka" rel="noopener noreferrer"&gt;Wikipedia entry,&lt;/a&gt; the Kafka platform was named after writer Franz Kafka and that was because, being a system optimized for writing, the creators thought it was fitting to use the name of a writer. In addition, Kafka is well resonating name for a software library — short, interestingly sounding, easy to remember. But Kafka also happens to be a well fitting name for a system that operates with message queues and events for more reasons than just Franz Kafka being a writer.&lt;/p&gt;

&lt;p&gt;As it happens, he had a peculiar affinity with messages and communication. In 1917, Kafka wrote a short story entitled &lt;a href="https://www.nybooks.com/daily/2011/07/01/message-emperor-new-translation/" rel="noopener noreferrer"&gt;&lt;em&gt;Message from the Emperor&lt;/em&gt;&lt;/a&gt;, in which a subject is entrusted with an urgent message at the bedside of a dying emperor. In the piece, which is barely one page long and presented as a parable, the messenger, who is an “indefatigable man” travels tirelessly through crowds, then stairways and courtyards again and again for thousands of years, never reaching its destination.&lt;/p&gt;

&lt;p&gt;Messages are also themes in some of Kafka’s other works, such as “&lt;a href="https://en.wikipedia.org/wiki/In_the_Penal_Colony" rel="noopener noreferrer"&gt;In the Penal Colony&lt;/a&gt;”, where the message is inscribed on the back of a prisoner by a brutal apparatus or &lt;a href="https://en.wikipedia.org/wiki/Amerika_(novel)" rel="noopener noreferrer"&gt;Amerika&lt;/a&gt;, which is set in an ultra-modern New York City and where communication is central to the story.&lt;/p&gt;

&lt;p&gt;By now you have probably concluded that this particular article will not be diving deep into the workings of the Apache Kafka software platform. If you’re a software engineer and you happen to be reading this while you’re trying to get more familiar with the system, you might see this entire story as some kind of literary poppycock which is completely irrelevant to what you were looking for. If you're a literary oriented person you are probably equally disillusioned, though you will most likely beware to not purchase a ticket to that &lt;a href="https://kafka-summit.org/" rel="noopener noreferrer"&gt;Kafka Summit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By now you might feel a bit annoyed and potentially even revolted. Even more so if I tell you that I have never used Apache Kafka. I had used RabbitMQ for some time, which I believe is a competitor platform, but I am not an expert on it. Nor am I an expert in the literary works of Franz Kafka for that matter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bbc.co.uk/sounds/play/b05tq3rg" rel="noopener noreferrer"&gt;The Essay - In the Shadow of Kafka - Waving or Drowning: Kafka and Meaning - BBC Sounds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;a href="https://www.mod-langs.ox.ac.uk/people/karen-leeder" rel="noopener noreferrer"&gt;Karen Leeder&lt;/a&gt; — translator and professor of Modern German Literature at the University of Oxford — is an expert by all accounts and she notes in a 2015 &lt;a href="https://www.bbc.co.uk/sounds/play/b05tq3rg" rel="noopener noreferrer"&gt;essay for BBC Radio 3&lt;/a&gt; that Kafka did write a lot of letters and he was paranoid with whether the letters will ever reach their destination. Somewhat in contrast with the software platform Apache Kafka which does everything possible in order to send those messages and has elaborate mechanisms to guarantee not only their arrival but also their acknowledgement.&lt;/p&gt;

&lt;p&gt;But what would Franz Kafka think about his name being used by a software platform? No doubt, he would find it terribly funny, as he did find his works later in his life. Although not particularly comical, some of them do bear a surreal or absurd aspect that could be seen as amusing. Or maybe he would think the whole matter is &lt;em&gt;kafkaesque&lt;/em&gt;. And if you’re still not convinced, consider this last sentence from &lt;em&gt;Message from the Emperor&lt;/em&gt; which is suddenly directed at the reader: “&lt;em&gt;You, however, sit at your window and dream of the message when evening comes”.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>kafka</category>
      <category>introduction</category>
      <category>meta</category>
      <category>learning</category>
    </item>
    <item>
      <title>On Improving Communication Inside a Dev Team</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Thu, 21 Mar 2019 21:49:34 +0000</pubDate>
      <link>https://dev.to/andreirusu_/on-improving-communication-inside-a-dev-team-cn1</link>
      <guid>https://dev.to/andreirusu_/on-improving-communication-inside-a-dev-team-cn1</guid>
      <description>&lt;p&gt;I have been working as a front-end engineer for about 15 years. I have started as a junior PHP developer in my university town of &lt;a href="https://en.wikipedia.org/wiki/Ia%C8%99i"&gt;Iași&lt;/a&gt; in Romania, and gradually moved further away from home afterwards. Throughout the years I have worked for companies in Bucharest, Nicosia, Amsterdam and, for the past five years, in Oslo. &lt;/p&gt;

&lt;p&gt;While in the different jobs I noticed one particular aspect of the job has been more or less consistent - disagreements between developers within the same team. As a developer it's probably quite easy to get into an argument with another team mate. And during the disagreement, all parties involved are very convinced that they are right. They all think that their solution is the best one for everyone involved and they will insist on their path. Usually a compromise will be reached, but they will still believe that their solution was the correct one, even if it wasn't selected by the majority.&lt;/p&gt;

&lt;p&gt;But no matter the outcome of the disagreement, the important thing is in my experience how the entire process of settling the dispute is handled by all parties involved. Most of the times, the parties will prefer to keep things between themselves and not involve external mediators, which is of course the ideal situation. When a mediator does intervene, they will only do so in order to make a decision, but the tension in the team will most likely remain.  &lt;/p&gt;

&lt;p&gt;The team members need to be able to resolve disputes in a civilized and friendly tone as possible, regardless if they feel friendly towards each other or not. This will not only have a positive impact for the rest of the team and organisation, but also for their own morale as employees and members of the team. Throughout the years, and also having participated in a few communication training programs, I have built a small set of techniques, in order to try and have a smooth communication with the other team members. Most of them will sound as common-sense stuff, but here goes:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Avoid: &lt;em&gt;"I want"&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;I hear this a lot from other people in the team - "I want to have this or that because...". And they might give a good reason or explanation, but the problem here is that they put the emphasis on what they want and this might make other people feel uncomfortable.&lt;/p&gt;

&lt;p&gt;The important thing to remember is, in my opinion, that even if it's a one person team, in most cases is not about what they want. It is about what is good for the team and the organisation. That does not mean that what the team members want is not important, but when someone says "I want" the other person doesn't usually hear collaboration.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Try this instead&lt;/u&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I think we should do it this way, because..."&lt;/em&gt; or &lt;em&gt;"I am confident that this is the way to go forward..."&lt;/em&gt; and so on... &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Avoid: &lt;em&gt;"You're wrong"&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;This is maybe even more important one to avoid as it is more effective at damaging your relationship with your colleagues. Or with anyone for that matter.&lt;/p&gt;

&lt;p&gt;So this &lt;strong&gt;must be avoided&lt;/strong&gt; at all costs. There is no place for it in a team who has respect as one of its core values. Of course, people are sometimes (oftentimes?) wrong. And if you need to communicate that to them you should do so by using empathy and respect. &lt;/p&gt;

&lt;p&gt;&lt;u&gt;Try this instead&lt;/u&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I'm not sure about that, because..."&lt;/em&gt; or &lt;em&gt;"I'm not convinced that's the best way"&lt;/em&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes you may require something stronger, but still respectful and not harmful, although it shouldn't be used lightly: &lt;em&gt;"I disagree"&lt;/em&gt;. And there are variations, depending of the intended tone and nature: &lt;em&gt;"I respectfully disagree"&lt;/em&gt; or &lt;em&gt;"I strongly disagree"&lt;/em&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Avoid criticizing the work of your team mates
&lt;/h3&gt;

&lt;p&gt;There's a myth existing for a while in development teams, which goes a bit like  "you are not your code". That is presumably meant to say that you shouldn't feel too bad when things go wrong as a result of your code changes. But sometimes it is also used as a platform for offering criticism.&lt;/p&gt;

&lt;p&gt;Personally, I never found expressions like "constructive criticism" to be useful. In my view, "constructive criticism" is an oxymoron - you can't be constructive if what you're doing is criticizing someone's work (or someone). &lt;/p&gt;

&lt;p&gt;Instead of offering criticism, we should be offering improvements. &lt;/p&gt;

&lt;p&gt;&lt;u&gt;Try this instead&lt;/u&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I think we can improve this by doing this or that..."&lt;/em&gt; or &lt;em&gt;"I believe this might not work in some/all cases. Here's why..."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Dealing with unpleasant situations
&lt;/h3&gt;

&lt;p&gt;Sometimes you may find yourself in an unpleasant situation and usually the best way is to deal with it is to confront your co-worker, if the situation allows, at least as a first step. They might not have meant any harm or they might not realize that their behaviour or remarks were harmful or distasteful. &lt;/p&gt;

&lt;p&gt;I have attended a communication seminar some time ago when I was working in Amsterdam. I didn't care too much of it, but the main takeway was quite good advice. They suggested your response when someone did or said something which bothered you in some way should be summarized in three main points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;start by saying what happened, from an objective point of view - or what your impression is;&lt;/li&gt;
&lt;li&gt;state how it made you feel - this doesn't need to show any personal emotions necessarily, it can be something like disappointed or confused;&lt;/li&gt;
&lt;li&gt;state what you would wish it would happen next - offer some kind of solution to remedy the problem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's an example where one colleague tells you to stop complaining and get on with your work, for instance. You might feel a little put off but hearing that you are complaining, when what you were trying was to highlight a problem that was bothering you. So one response could be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I see you're saying that I'm complaining. That is something I normally try to avoid so I'm a bit disappointed by hearing this. I still think it's important that we address this issue and try and find a solution because it is bothering me."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Personally, I don't always feel that confrontational. I wish I would be, but in situations where I think I should have said something and didn't, I always have the option to send them an email. In it I can be as detailed as I want and I can express my opinion uninterrupted and concisely as I see fit.&lt;/p&gt;

&lt;p&gt;Sending an email might not sound like the most courageous strategy, but it is of course what &lt;a href="https://en.wikipedia.org/wiki/Franz_Kafka"&gt;Franz Kafka&lt;/a&gt; did as well when he attempted to confront his absolutely unimpressive father by sending him the &lt;a href="https://en.wikipedia.org/wiki/Letter_to_His_Father"&gt;famous letter&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Don't hold grudges
&lt;/h3&gt;

&lt;p&gt;This is always great advice, regardless of the situation. And it seems that actor, author and podcaster Russel Brand also &lt;a href="https://twitter.com/rustyrockets/status/1108470516904411137"&gt;has the same advice&lt;/a&gt;, and he probably will be more convincing.&lt;/p&gt;

</description>
      <category>team</category>
      <category>dev</category>
      <category>communication</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Dear [Applicant]</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Sat, 16 Mar 2019 12:59:13 +0000</pubDate>
      <link>https://dev.to/andreirusu_/dear-applicant-130k</link>
      <guid>https://dev.to/andreirusu_/dear-applicant-130k</guid>
      <description>&lt;p&gt;This is an interview rejection letter template to be used as needed by companies. I’ve included a few passages from various rejection letters of my own.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dear [Applicant’s First Name],&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you for completing the interview for the [position name] job with us. After careful evaluation in collaboration with the hiring team, we regret to inform you that we won’t be moving forward with your application, at this time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As you can imagine, there is a lot of competition for the role and we can only take forward the people we think would be most suitable. While we are convinced that you are perfectly skilled for this position and you appear to have enough academic qualifications, we still somehow feel that you are not the right candidate. You seem like someone who would get in an unpleasant argument with someone from the management team at some point in the future. Surely you understand that we would prefer to avoid these type of situations, it serves no one any good.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We’re also grateful that you took the time and complete our 4+ hours assignment so skilfully that we even got some ideas from it. We would like to encourage you to re-apply for this role in the future, should you still be interested, however please bear in mind that your chances of success will still be limited.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;New opportunities arise every day to help us make real what matters. Please don’t let this stop you from applying for a different role, at different company.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We wish you the best of luck with your career, wherever it may take you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yours truly,&lt;/em&gt;&lt;br&gt;
&lt;em&gt;The Company&lt;/em&gt;&lt;/p&gt;

</description>
      <category>interview</category>
      <category>rejection</category>
      <category>template</category>
      <category>career</category>
    </item>
    <item>
      <title>Coding Assignments for Job Interviews Are Obsolete</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Thu, 21 Feb 2019 23:12:35 +0000</pubDate>
      <link>https://dev.to/andreirusu_/why-coding-assignments-for-job-interviews-are-obsolete-4k64</link>
      <guid>https://dev.to/andreirusu_/why-coding-assignments-for-job-interviews-are-obsolete-4k64</guid>
      <description>&lt;p&gt;As someone who maintains a relatively popular open-source project on Github for the past 5 years I am becoming increasingly skeptical of companies who ask me to do a coding assignment as part of the interview process. And I do interviews quite often, for one reason or another.&lt;/p&gt;

&lt;p&gt;Most companies will ask me to do a coding assignment after the initial phone/skype chat, if successful. Others will send a coding assignment right away after just reviewing the application, even before a phone screening. There are organisations who claim that they are receiving a large amount of applications and so this is a practical solution for them. I’ve got such an assignment following an application for a full-stack developer position at CERN, which evidently was not successful.&lt;/p&gt;

&lt;p&gt;However, in my experience, the most common scenario is when I receive the assignment after the phone interview. And the said assignment need not take more than 3 or 4 hours usually. There is even a platform to facilitate this — coderpad.io, a website which aims to provide a live coding environment specifically designed for job interview assignments. Fair enough, the companies need a suitable and effective tool to hire the best candidates they can find, and faster. Right? Wrong.&lt;/p&gt;

&lt;p&gt;We don’t need coding assignments anymore. Coding assignments are a thing of the past and in most cases they don’t prove much, as they are quite generic and most of them lack innovation. Even the CERN assignment, of which one would have high expectations, was rather disappointing in terms of creativity. I still failed it though, but that is beside the point.&lt;/p&gt;

&lt;p&gt;Now we have a much better tool at our disposal. And it works well for everybody, not just for the companies hungry for talent. It’s the Open-Source community. Instead of the coder doing an assignment which has a high chance of being rejected and thus wasteful, organisations need to simply pick an open-source project on Github and ask the interviewee to submit a pull request for it. Even better, it could be a project that the company uses. Clearly this much more beneficial for the interviewee in the event of an unsuccessful interview as well. In the regular assignment situation, the coder will at best receive a few lines as feedback which is unlikely to be very useful.&lt;/p&gt;

&lt;p&gt;Coding assignments are also alienating, stressful, inconclusive. We don’t need that in our lives. Pull requests are useful, empowering, inclusive and transparent. And a platform like Coderpad could work really well here to facilitate things. As a maintainer, I would be very happy to assist them with indicating which Github issues would be most suitable as an assignment.&lt;/p&gt;

&lt;p&gt;I recently was in a situation where I have received a 3 hour assignment. This was my response:&lt;/p&gt;

&lt;p&gt;Hi {Intervieweer Name},&lt;/p&gt;

&lt;p&gt;With respect, 3 hours is quite a long time to ask an interviewee to spend for a coding test. I mean, I have been developing this open-source project for the past 5 years. All the source code is on Github at […] and to be frank I think that is a good indication that I am able to code in Javascript. You can check the source code for {Project}, it has also around 85% test coverage, most of the codebase is written (or reviewed) by me. In addition, you have my CV with presumably enough experience and academic qualifications for this position.&lt;/p&gt;

&lt;p&gt;So it is up to you now to decide if you want to move forward with this. Personally, I don’t think it would be very productive for me to spend 3 hours to do a coding test for a job interview when I could very well spend that time fixing some bugs for an open-source project that the community can benefit from. I hope you understand.&lt;/p&gt;

&lt;p&gt;Regards,&lt;br&gt;
Andrei&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>codinginterviews</category>
    </item>
    <item>
      <title>Not a Cultural Fit</title>
      <dc:creator>Andrei Rusu</dc:creator>
      <pubDate>Thu, 02 Aug 2018 15:31:48 +0000</pubDate>
      <link>https://dev.to/andreirusu_/not-a-cultural-fit-25g5</link>
      <guid>https://dev.to/andreirusu_/not-a-cultural-fit-25g5</guid>
      <description>&lt;p&gt;About five years ago I was interviewing in Amsterdam for a software engineering position at one of the largest telecom companies in Norway. It was a casual style interview, with two members of the development team, mostly focusing on technical matters, both regarding the position and my professional background. We spoke about one hour and I was left with the impression that the interview went quite well. There were no uncomfortable questions, no long pauses and no gaffes on my side that I could think of. I was quite certain that the process will advance to the next stage.&lt;/p&gt;

&lt;p&gt;A few days go by and no sign. I was starting to doubt my overly optimistic evaluation of the situation. Eventually I get an email response from someone at the HR department saying the two interviewers didn’t feel I was a good fit for the position. I was a bit surprised to hear that. By this time I was expecting a rejection, but the feedback left me quite puzzled. So I followed up with an email asking if there’s any chance I get a more detailed explanation on why I was rejected, to which I got the reply that the two developers who interviewed me felt I have good enough technical skills, but they didn’t feel I would be a good &lt;em&gt;cultural fit&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And that’s that. It’s the most dreaded rejection feedback which an introvert like myself, with a healthy amount of insecurity, knows it cannot be challenged or contested. This so-called &lt;em&gt;cultural fit&lt;/em&gt; is not something that you can have a say in, or something you can achieve, whether that is something you want to or not. Also it’s not something you can actively train for, like when you’re rejected because “you don’t have enough experience”, or “you don’t have this or that skill”, or even when they need “someone with leadership skills”. If you’re not a cultural fit, that’s rather permanent, and it’s not like you can go out and acquire some and apply for the job next year, mostly because the “culture” is a very loose term in this case and you’re not sure what it is exactly, but you know that you don’t fit in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AnZk5Dd2wjoP-GwxqV-gOOw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AnZk5Dd2wjoP-GwxqV-gOOw.jpeg"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/srDN5nHM35g?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Justin Main&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Company Culture
&lt;/h3&gt;

&lt;p&gt;It is up to the organisation to make the decision if someone is a cultural fit, based on its own values, policies, and other considerations which collectively make up the company culture, although these are not always clearly defined. So in order to be accepted, one must not only have the skills and qualifications required for the position, but also meet the company’s expectations in terms of cultural fitness. How this decision making process works though is not always transparent, one might say even arbitrary. The candidate can be rejected on the basis of cultural unfitness even when they are applying for a position inside a team or department which is currently being formed, and in which case it is unlikely that the culture is well defined.&lt;/p&gt;

&lt;p&gt;The company culture varies to a large degree from organisation to organisation, but most of them have one, either explicitly defined or more indirectly shaped. There is a growing trend in the tech industry where the organisation believes that it needs to think it through and decide its own culture. Based on this, the company will review candidates to fill positions, and these candidates will not only have to be qualified but also will need to possess a certain combination of personality traits that are presumed to contribute to the individual fitting in. Some companies will put it forward on its website or blog, others will be more subtle about it towards potential hires. Sometimes the company will discuss its culture with the interviewee as part of the hiring process, in an attempt to make it easier for both parties to decide if there’s a match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cultural Fit Recruitment
&lt;/h3&gt;

&lt;p&gt;The culture seems to emerge at a stage when there’s a large enough degree of prosperity and success in the company, often as part of its expansion process. Over the past decade an idea has started to take shape, especially in the tech industry, that companies should attempt to hire for “cultural fit” and many of them seem to believe that culture fit is arguably more important than skills or experience. The argument is that a poor culture fit will lead to higher employee turnover, to speak in business terms, and that can be costly for an organisation, with the &lt;a href="https://www.shrm.org/" rel="noopener noreferrer"&gt;Society for Human Resource Management (SHRM)&lt;/a&gt; placing this cost in their &lt;a href="https://www.shrm.org/hr-today/trends-and-forecasting/special-reports-and-expert-views/Documents/Retaining-Talent.pdf" rel="noopener noreferrer"&gt;&lt;em&gt;2008 Retaining Talent&lt;/em&gt;&lt;/a&gt; report, and using as reference the &lt;em&gt;“Managing Human Resources: Productivity, Quality of Work Life, Profits”&lt;/em&gt; book by Wayne Cascio, at 50%-60% of an employee’s annual salary.&lt;/p&gt;

&lt;p&gt;There’s a growing number of articles, many of them from business journals such as Forbes or Harvard Business Review, advocating the practice of hiring for culture fit, such as this one from 2015: &lt;a href="https://hbr.org/2015/07/recruiting-for-cultural-fit." rel="noopener noreferrer"&gt;Recruiting for Cultural Fit&lt;/a&gt;. This type of literature is suggesting that in order for an employee to be successful in an organisation there needs to be a strong match between the organisation’s values, ethics, and beliefs and those of the employee.&lt;/p&gt;

&lt;p&gt;The key component of a good “culture fit” can be directly linked to employee happiness, and the idea is that a happy employee is more productive, and that will ultimately lead to more profit. It’s a win-win. Companies believe that fostering the well-being of their employees will contribute to their success, and &lt;a href="https://www.shrm.org/hr-today/trends-and-forecasting/research-and-surveys/Documents/2016-Employee-Job-Satisfaction-and-Engagement-Report-Executive-Summary.pdf" rel="noopener noreferrer"&gt;research by the SHRM&lt;/a&gt; shows that promoting policies which will increase employees feelings of well-being, such as respectful treatment at all levels, will ultimately lead to greater happiness levels and overall better job satisfaction. There’s plenty of other sources which indicate that &lt;a href="https://theconversation.com/how-happiness-improves-business-results-64806" rel="noopener noreferrer"&gt;&lt;em&gt;happiness improves business results&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unsurprisingly though, employee happiness is also determined to a large extent by the individual’s own personality, as &lt;a href="https://www.robertsoncooper.com/download/latest-research/could-your-personality-be-affecting-how-many-good-days-at-work-you-have" rel="noopener noreferrer"&gt;research carried out by Robertson Cooper Ltd&lt;/a&gt;, a workplace wellbeing consultancy, indicates. They have found that employees who scored high on positive emotions and enthusiasm, and lower on depressive tendencies, will experience more “good days at work” than the others.&lt;/p&gt;

&lt;p&gt;If a key component of a successful “culture fit” is personality, then, to a significant degree, specific traits would be those which indicate a relatively happy individual. A less than happy person could not only have problems with adapting and being productive, but also be a negative effect on the rest of the team. Whenever there’s a poor cultural fit, the overall morale of the team is affected, and inevitably is the effectiveness as well. At this point, it’s relatively safe to assume that organisations prefer, for the most part, homogeneity vs eccentricity. For this reason there’s a good amount of effort put by a growing number of organisations into screening candidates for cultural fitness.&lt;/p&gt;

&lt;p&gt;A candidate, on the other hand, will attempt to put their best face forward, when interviewing for a new job, or a promotion, in order to maximise the possibility of acquiring the position. This, however, poses a problem of imbalance between people’s behaviour and the emotional states they want to appear they are feeling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medicalxpress.com/news/2018-07-emotions-result-hiring-workplace-bias.html" rel="noopener noreferrer"&gt;A new study&lt;/a&gt; by researchers at Stanford, published in the &lt;em&gt;Emotions&lt;/em&gt; journal, found that &lt;em&gt;“what is interpreted as the ‘best impression’ varies from person to person and from culture to culture”&lt;/em&gt;. The study further suggests that the practice of recruiting for cultural fit may lead to hiring bias. This finding doesn’t seem to be much of surprise, with various organisations already starting to voice their discontent with the practice, such as Buffer.com, which instead advocates on their &lt;a href="https://open.buffer.com/culture-fit/" rel="noopener noreferrer"&gt;Open blog&lt;/a&gt; for a slightly different approach called &lt;em&gt;“cultural add”&lt;/em&gt;. Facebook also has their own response for &lt;a href="https://newsroom.fb.com/news/2015/07/managing-unconscious-bias/" rel="noopener noreferrer"&gt;managing unconscious bias&lt;/a&gt;, an internal training programme which they have &lt;a href="https://managingbias.fb.com/" rel="noopener noreferrer"&gt;made public&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Nokia Moment
&lt;/h3&gt;

&lt;p&gt;Is the company culture really that important for an organisation success? And if so, how far should the organisation go in terms of applying it? A recent BBC4 documentary called &lt;a href="https://www.bbc.co.uk/programmes/b0b9kj80" rel="noopener noreferrer"&gt;The Rise and Fall of Nokia&lt;/a&gt; reveals that in its heyday, a core value of Nokia used to be “Respect for people”. But as Nokia was found itself with huge, unexpected success, a shift in values also started to emerge. The film further shows how Nokia’s enormous, quite sudden, success also started to attract a large number of people who might have been drawn to the organisation just because they were looking for success and financial gain, rather than making a worthwhile contribution. This has overtime led to the core value being reduced to just “Respect” alone, which arguably has played a role in Nokia’s eventual inability to further innovate.&lt;/p&gt;

&lt;p&gt;So could this be a case of too much company culture? Successful companies which are now in a position of prosperity, are very weary about their success and how long it might last, fearing a &lt;em&gt;Nokia moment&lt;/em&gt; which draws analogies with the moment in which Nokia was just before the release of the iPhone.&lt;/p&gt;

&lt;p&gt;I can also report that I may have witnessed a Nokia moment in one of my previous jobs in my career. Back in Amsterdam for about five years I have worked for a start-up which was developing a web and mobile chat messenger platform. A very successful product by all accounts, with a large user base and popularity, reaching its peak sometime in 2009 when profitability was at an all-time high, the engineering team operating as a well-oiled machine, and overall employee job satisfaction was soaring. So the company was expanding and, as a result, the company culture was also being shaped more clearly. The core values and principles were emerging, along with various management level position changes. By 2011, the general attitude has changed, the core companies values were prominently displayed and circulated internally, and these changes seemed to have coincided with a decline in the chat messaging market.&lt;/p&gt;

&lt;p&gt;The advent of the iPhone and other smartphones gave birth to a new sort of messaging platform, the most prominent one being WhatsApp, which provided new and exciting capabilities for mobile messaging over traditional chat platforms, such as MSN or Yahoo. My employer was found with a very difficult problem to solve, to adapt to the decline of the market in which they were operating. A new platform was needed, one which will effectively take on WhatsApp, in all its glory. Much like Nokia needed a strong competitor for the iPhone, and just as Nokia came up with the MeeGo platform, they also came up with a competitor for WhatsApp, however it didn’t quite work very well in either cases.&lt;/p&gt;

&lt;p&gt;Looking back now, one has to ask oneself if there’s any link between a well-defined company culture and promoting creativity and innovation. Do they play well together? It doesn’t seem to be an idea that André Spicer, a professor at the Cass Business School in London, would agree with, as he writes in &lt;a href="https://aeon.co/essays/you-don-t-have-to-be-stupid-to-work-here-but-it-helps" rel="noopener noreferrer"&gt;his essay published in the Aeon magazine&lt;/a&gt;: &lt;em&gt;“Often, these cultures imprison employees in narrow ways of viewing the world, such as the common obsession with constant change”.&lt;/em&gt; Spicer has also authored or co-authored a series of books, including &lt;em&gt;The Stupidity Paradox (2016)&lt;/em&gt; and &lt;em&gt;Business Bullshit (2018)&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The One and The Many
&lt;/h3&gt;

&lt;p&gt;Hiring for culture fit doesn’t seem to play well with diversity and inclusion hiring practices either, which not only make up the recruitment rule of thumb in terms of morality and ethics, but also in terms of legality, at least in democratic countries which have enacted laws for equal opportunity employment. In the US, under the &lt;a href="https://www.ada.gov" rel="noopener noreferrer"&gt;&lt;strong&gt;Americans with Disabilities Act (ADA)&lt;/strong&gt;&lt;/a&gt;, &lt;em&gt;“employers cannot use eligibility standards or qualifications that unfairly screen out people with disabilities and cannot make speculative assumptions about a person’s ability to do a job based on myths, fears, or stereotypes about employees with disabilities (such as unfounded concerns that hiring people with disabilities would mean increased insurance costs or excessive absenteeism)”&lt;/em&gt;. And mental health issues, such as depression, bipolar disorder, post traumatic stress disorder (PTSD), are &lt;a href="https://www.eeoc.gov/eeoc/publications/mental_health.cfm" rel="noopener noreferrer"&gt;&lt;strong&gt;recognised disabilities under the ADA&lt;/strong&gt;&lt;/a&gt;, and more importantly, are non-obvious during an interview.&lt;/p&gt;

&lt;p&gt;This leads to the conclusion that evaluating a person’s happiness levels during an interview and using this type of information to determine if that person is a good fit for the position, based on how they might appear emotionally, is not only immoral, but also possibly illegal, under the &lt;strong&gt;&lt;em&gt;ADA&lt;/em&gt;&lt;/strong&gt;. In Britain mental health issues are protected against discrimination under the &lt;a href="https://www.gov.uk/rights-disabled-person/employment" rel="noopener noreferrer"&gt;&lt;strong&gt;Equality Act 2010&lt;/strong&gt;&lt;/a&gt;, with mental health conditions that are defined as impairment being listed under &lt;em&gt;Section A&lt;/em&gt; of the &lt;a href="https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/570382/Equality_Act_2010-disability_definition.pdf" rel="noopener noreferrer"&gt;Guidance document&lt;/a&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AWqU5k0Ox0m4EXdTo69-iRA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AWqU5k0Ox0m4EXdTo69-iRA.jpeg"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/6DXT79UXikY?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Margo Brodowicz&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Could the solution then be to remove the &lt;em&gt;culture&lt;/em&gt; component from an organisation recruitment practices entirely? What would that look like? Companies would be left with only qualifications, academic background or experience to judge candidates with. And they would have no choice but to &lt;em&gt;trust&lt;/em&gt; that new hires will be a good fit, because if someone doesn’t fit it in a team or a company, there’s probably a reasonable explanation of why that is the case.&lt;/p&gt;

&lt;p&gt;Eliminating the culture layer will leave only trust and true diversity &amp;amp; inclusion policies as the baseline, and these should be the only acceptable recruitment practices. This might sound like a concept straight from a utopian novel and well, quite frankly, it is. The world imagined by William Morris in his &lt;strong&gt;&lt;em&gt;News from Nowhere (1890)&lt;/em&gt;&lt;/strong&gt; novel is directly associating human happiness with economic activity, and how some of his ideas, not only have practical applications in the 21st century’s &lt;em&gt;“age of the Internet”&lt;/em&gt;, as &lt;a href="https://aeon.co/essays/why-the-utopian-vision-of-william-morris-is-now-within-reach" rel="noopener noreferrer"&gt;this Aeon.co essay&lt;/a&gt; explains, but also how the world seems to have caught up with them.&lt;/p&gt;

&lt;p&gt;If recruiting for cultural fit is a practice fraught with problems, then maybe the whole idea of company culture is a failing concept. This might be a classic case of the philosophical problem &lt;strong&gt;&lt;em&gt;"The One and The Many"&lt;/em&gt;&lt;/strong&gt; , a concept which was recently debated at length at the &lt;a href="https://www.bbc.co.uk/programmes/b09vllb4" rel="noopener noreferrer"&gt;2018 Free Thinking Festival&lt;/a&gt; by the BBC.&lt;/p&gt;

&lt;h3&gt;
  
  
  The quitting economy
&lt;/h3&gt;

&lt;p&gt;One possible take on this problem could result from a concept which is already gaining a lot of attention in the business and economic space at the moment: decentralisation. If one were to apply this to a company and its employees, then the result would probably be the so-called gig economy or the quitting economy. The concept in which these trends originate is based on the ideas put forward by influential Austrian economist Friedrich Hayek (1899­-1992) and other members The &lt;a href="https://www.montpelerin.org/" rel="noopener noreferrer"&gt;Mont Pelerin Society&lt;/a&gt;, commonly referred to as neoliberalism. This school of thought argues that individual employees, existing or prospective, should think of themselves as businesses, an idea which is analysed at depth by &lt;a href="https://www.indiana.edu/~amst/NAIS/gershon.shtml" rel="noopener noreferrer"&gt;&lt;strong&gt;Ilana Gershon&lt;/strong&gt;&lt;/a&gt;, associate professor of anthropology at Indiana University, Bloomington, in her Aeon essay, &lt;a href="https://aeon.co/essays/how-work-changed-to-make-us-all-passionate-quitters" rel="noopener noreferrer"&gt;The quitting economy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this kind of environment, which seemingly makes the company culture irrelevant, the emphasis is place on the individual (The One), rather than the organisation (The Many). When an organisation starts to grow and develop its own culture that will later serve as a screening tool for new hires to be accepted, it is also, in many cases, creating a closed circle environment. A type of exclusionary society in which bias thrives, and to which the individual has to adapt in order to be accepted. But in the quitting economy these types of incentives for motivating the employee, who is either an independent contractor, or someone preparing for the next job, are no longer relevant, nor effective.&lt;/p&gt;

&lt;p&gt;Gershon further indicates in her essay that the driving force now is &lt;strong&gt;passion&lt;/strong&gt; — how passionate an individual is about their work — and it goes on to suggest that &lt;em&gt;"in the quitting economy, you have to work for passion, and working for passion means focusing on the task, not the company"&lt;/em&gt;. A manager role is no longer to make sure that an employee is fitting well into the team, but rather to focus on making sure that the employee is able to do their job well, and also to assist them on making the transition to the next job, whenever that might be. Companies now accept that the individual doesn’t really work for the company, they work for themselves and for their passion, and building a career in a company is not something the individual should aspire to.&lt;/p&gt;

&lt;p&gt;Even if this thinking might sound a bit too radical for most people to digest, with immediate challenges such as job security and stability, the potentially constant fatigue of having to find a next job, or maybe a lack of feeling that the individual belongs to a group, the initial effect will probably be a shift in the corporate culture narrative, and a transition to a more inclusive and diversified organisational structure.&lt;/p&gt;

</description>
      <category>interviewing</category>
      <category>recruiting</category>
      <category>career</category>
      <category>culture</category>
    </item>
  </channel>
</rss>
