<?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: Ema Suriano</title>
    <description>The latest articles on DEV Community by Ema Suriano (@emasuriano).</description>
    <link>https://dev.to/emasuriano</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%2F37767%2Fb63013e1-6aee-4658-ba81-4adb4669ef2f.jpg</url>
      <title>DEV Community: Ema Suriano</title>
      <link>https://dev.to/emasuriano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emasuriano"/>
    <language>en</language>
    <item>
      <title>Get Github repository preview images</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Thu, 26 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/get-github-repository-preview-images-11k4</link>
      <guid>https://dev.to/emasuriano/get-github-repository-preview-images-11k4</guid>
      <description>&lt;p&gt;While I was writing another &lt;a href="https://dev.to/til"&gt;TIL&lt;/a&gt; I needed to put some image about an open source project, in this case, was the popular project of &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. Normally what I do is take a screenshot of the website and store it locally in my assets folder, which is what most of the websites also do.&lt;/p&gt;

&lt;p&gt;The main issue of this problem is not the fact that the repository size is going to grow (I solved this issue by using Git submodules), it is that the image most probably is going to get out of date. The information about the post is most probably also going to get outdated, but having as well old pictures doesn't help a lot ...&lt;/p&gt;

&lt;p&gt;While I was searching for some kind of image that represents the project without information that can get outdated, I found this preview:&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%2Fopengraph.githubassets.com%2F16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04%2Follama%2Follama" 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%2Fopengraph.githubassets.com%2F16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04%2Follama%2Follama" alt="Ollama Github preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It caught my attention because all the information was extremely up-to-date, considering that the last commit was less than 12 hours ago.&lt;/p&gt;

&lt;p&gt;Then I decided to inspect the URL and, I found out that, it was coming from the &lt;a href="https://www.opengraph.xyz/" rel="noopener noreferrer"&gt;Open Graph&lt;/a&gt; of Github assets. The URL itself looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://opengraph.githubassets.com/16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04/ollama/ollama

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

&lt;/div&gt;



&lt;p&gt;Then I checked on the Internet what each part means, and I found the following explanation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://opengraph.githubassets.com/&amp;lt;random_hash&amp;gt;/&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;After knowing this I started playing with some of my other repositories and I found that I can always reliably get an up-to-date Github preview of my projects. As an example, I generated the one for this website:&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%2Fopengraph.githubassets.com%2Ftest%2FEmaSuriano%2Fportfolio" 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%2Fopengraph.githubassets.com%2Ftest%2FEmaSuriano%2Fportfolio" alt="Portfolio Github preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That will be all for now.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running local LLM is not as hard as I expected</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Thu, 26 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/running-local-llm-is-not-as-hard-as-i-expected-1paj</link>
      <guid>https://dev.to/emasuriano/running-local-llm-is-not-as-hard-as-i-expected-1paj</guid>
      <description>&lt;p&gt;Let's keep it short, if you don't have a dedicated GPU you can still run locally an LLM and keep your data private to you, or even be creative and integrate it into an application or your workflow without having to pay per generated token (which is the way people pay for the closed-source LLM).&lt;/p&gt;

&lt;p&gt;The open-source project that will make your life easier is called &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&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%2Fopengraph.githubassets.com%2F16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04%2Follama%2Follama" 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%2Fopengraph.githubassets.com%2F16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04%2Follama%2Follama" alt="Ollama Github preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ollama can be described as an LLM manager, with which you can easily install and run models locally on your computer.&lt;/p&gt;

&lt;p&gt;The installation is pretty straightforward forward and it's now &lt;a href="https://ollama.com/blog/windows-preview" rel="noopener noreferrer"&gt;officially supported&lt;/a&gt; for all the Operative Systems: macOS, Linus, and Windows.&lt;/p&gt;

&lt;p&gt;After the installation is over the way you interact with Ollama is via the Terminal application, using the command &lt;code&gt;ollama&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ ollama
Usage:
  ollama [flags]
  ollama [command]

Available Commands:
  serve Start ollama
  create Create a model from a Modelfile
  show Show information for a model
  run Run a model
  stop Stop a running model
  pull Pull a model from a registry
  push Push a model to a registry
  list List models
  ps List running models
  cp Copy a model
  rm Remove a model
  help Help about any command

Flags:
  -h, --help help for ollama
  -v, --version Show version information

Use "ollama [command] --help" for more information about a command.

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

&lt;/div&gt;



&lt;p&gt;All the models you can install and run are listed on their official website on the &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;Library page&lt;/a&gt;. They are by default listed as "Featured", which tends to be from better to worse (but that can change).&lt;/p&gt;

&lt;p&gt;The most popular model to run with Ollama is &lt;a href="https://www.llama.com/" rel="noopener noreferrer"&gt;Llama&lt;/a&gt; from Facebook. At the moment of writing this article, they just announced their new model &lt;a href="https://ollama.com/library/llama3.2" rel="noopener noreferrer"&gt;llama3.2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install and run it, simply execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ ollama run llama3.2

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

&lt;/div&gt;



&lt;p&gt;The first time you run it, it will download the model and once everything is done you are going to see a prompt in your terminal saying "Send a message". This is the simplest way you can try the model and see how the responses are.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Hey what's up?
Not much! It's nice to meet you. Is there something I can help you with or
would you like to chat?

&amp;gt;&amp;gt;&amp;gt; I'm writing a post, can you send a short message to the readers :)
Here is a short and sweet message:

"Thank you for being here!"

Feel free to use it as is or modify it to fit your post's tone and style!

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Going a step further
&lt;/h2&gt;

&lt;p&gt;We can all agree that using the terminal to communicate with the model can be a bit cumbersome. Compared to other LLM experiences like Chat GPT, Claude, or even Bing, we are missing many features, such as History, the possibility of uploading images, file upload, sending audio, etc.&lt;/p&gt;

&lt;p&gt;This is the problem that Open WebUI is coming to solve!&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%2Fopengraph.githubassets.com%2F16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04%2Fopen-webui%2Fopen-webui" 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%2Fopengraph.githubassets.com%2F16dc0ce438cf338ca03e98e84238f23161d058f1665111075ba68fcd2f0ecf04%2Fopen-webui%2Fopen-webui" alt="Open WebUI Github preview"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's a project that runs on top of Ollama and allows you to interact with the models in many ways. You can see it as a front end of your Backend (Ollama).&lt;/p&gt;

&lt;p&gt;They ship tons of features so I highly recommend checking their websites where they explain all of them: &lt;a href="https://docs.openwebui.com/features" rel="noopener noreferrer"&gt;Features&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To &lt;a href="https://docs.openwebui.com/getting-started/#quick-start-with-docker--recommended" rel="noopener noreferrer"&gt;install the project&lt;/a&gt; the easiest way is to download the Docker image that gets automatically updated when they release a new version. For this, you have to install Docker on your machine if you don't have it yet.&lt;/p&gt;

&lt;p&gt;Then execute this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

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

&lt;/div&gt;



&lt;p&gt;This will download the image, install the required dependencies in the image, and start the daemon instance, which you can easily access by entering &lt;a href="https://localhost:3000" rel="noopener noreferrer"&gt;https://localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first time you are going to see a Login screen, just create an account. This is in case you want to host the server and would like to restrict access or split the history between the users.&lt;/p&gt;

&lt;p&gt;And now you can fully enjoy the full experience :)&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%2Fwea1ia2dpqfe2dt07fb6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwea1ia2dpqfe2dt07fb6.gif" alt="Open WebUI demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Remind yourself of Birthdays Using Google Calendar and Apps Script</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Mon, 05 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/remind-yourself-of-birthdays-using-google-calendar-and-apps-script-3ba0</link>
      <guid>https://dev.to/emasuriano/remind-yourself-of-birthdays-using-google-calendar-and-apps-script-3ba0</guid>
      <description>&lt;p&gt;If you keep track of friends' and family members' birthdays using Google Contacts, you can receive automatic email notifications on the day of their birthdays by integrating Google Calendar and Google Apps Script. Here’s how you can set it up:&lt;/p&gt;

&lt;p&gt;The first thing to do is to enable the Birthday Calendar. This can be done by enabling the calendar "Birthday" in Google Calendar, and you should see a calendar named "Contacts" inside "Other Calendars".&lt;/p&gt;

&lt;p&gt;Once turned on you should see the name of your contact along with a cake emojii (🎂), and they should be automatically repeated every year. This is the integration between Google Calendar and Google Contacts.&lt;/p&gt;

&lt;p&gt;The last thing we need before making the automation is to obtain the Calendar ID. For that click the three dots next to the "Birthdays" calendar, go to Settings and sharing, and find the Calendar ID. It might look like &lt;code&gt;#contacts@group.v.calendar.google.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that we can create our small script, so let's open Google Apps Script through &lt;a href="https://script.google.com" rel="noopener noreferrer"&gt;script.google.com&lt;/a&gt; or from any Google Sheet by navigating to Extensions &amp;gt; Apps Script. Use the following script to check for birthdays and send email reminders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var CALENDAR_ID = "#contacts@group.v.calendar.google.com"; // Use your Birthday calendar ID
var EMAIL = "your.email@example.com"; // Change this to your email

function sendBirthdayReminders() {
  var today = new Date();
  var events = CalendarApp.getCalendarById(CALENDAR_ID).getEventsForDay(today);

  events.forEach(function (event) {
    var title = event.getTitle();

    Logger.log("Sending email for event: " + title);
    MailApp.sendEmail({
      to: EMAIL,
      subject: "Birthday Reminder: " + title,
      body:
        "Today is " + title + "'s birthday! Don't forget to wish them well.",
    });
  });
}

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

&lt;/div&gt;



&lt;p&gt;You can run the script manually to ensure it’s working as expected. Make sure to authorize any permissions for the script to access your Calendar and Gmail.&lt;/p&gt;

&lt;p&gt;Lastly, we need to set up an automatic trigger to run the script every day. In the Apps Script editor, click on the clock icon to set up a trigger. Add a new time-driven trigger to run &lt;code&gt;sendBirthdayReminders&lt;/code&gt; daily at a specific time (e.g., 7:00 AM).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus Tip&lt;/strong&gt; : You can potentially customize the script to include special messages or even send reminders to multiple people if needed!&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Install Fuzzy CLIPS: A Quick and Easy Setup Tutorial</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Wed, 31 Jul 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/how-to-install-fuzzy-clips-a-quick-and-easy-setup-tutorial-424a</link>
      <guid>https://dev.to/emasuriano/how-to-install-fuzzy-clips-a-quick-and-easy-setup-tutorial-424a</guid>
      <description>&lt;p&gt;Fuzzy CLIPS is an extension of &lt;a href="https://www.clipsrules.net/" rel="noopener noreferrer"&gt;CLIPS&lt;/a&gt; (C Language Integrated Production System) designed for handling fuzzy logic. It allows systems to make decisions based on approximate or imprecise information.&lt;/p&gt;

&lt;p&gt;Fuzzy CLIPS is an older tool with limited and outdated online documentation. This is the main reason I'm writing this short post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Fuzzy Logic?
&lt;/h2&gt;

&lt;p&gt;Fuzzy logic excels at handling real-world complexity and imprecision, it allows you to model human-like reasoning, deal with ambiguous data, and create more intuitive control systems. By using degrees of truth instead of binary (&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;) decisions, fuzzy logic can provide smoother, more accurate results in situations where traditional logic falls short&lt;/p&gt;

&lt;p&gt;It's precious in fields like control engineering, decision support systems, and AI, which can lead to more robust and flexible solutions that better mimic human decision-making processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Fuzzy CLIPS
&lt;/h2&gt;

&lt;p&gt;The source code can be found in &lt;a href="https://github.com/rorchard/FuzzyCLIPS" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; but as expected, this cannot be directly executed in our system. We need to compile the program to run it locally.&lt;/p&gt;

&lt;p&gt;Let's start by cloning the repository:&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/rorchard/FuzzyCLIPS &amp;amp;&amp;amp; cd FuzzyCLIPS/source

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

&lt;/div&gt;



&lt;p&gt;Now we are going to make use of the &lt;code&gt;make&lt;/code&gt; command which is available by default in OSX and Linux. In case you are running Windows, please check to install using &lt;code&gt;choco&lt;/code&gt; (&lt;a href="https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows" rel="noopener noreferrer"&gt;reference&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ make fzclips

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

&lt;/div&gt;



&lt;p&gt;This should create an executable binary called &lt;code&gt;fzclips&lt;/code&gt; which we can execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./fz_clips

FuzzyCLIPS V6.10d (10/22/2004)
FuzzyCLIPS&amp;gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fuzzy Concepts
&lt;/h2&gt;

&lt;p&gt;To understand how Fuzzy Clips works, is better to do a small recap of some of the core concepts of Fuzzy Logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fuzzy sets&lt;/strong&gt; : extend classical set theory by allowing partial membership. In a classical set, an element either belongs to the set (1) or doesn't (0). In a fuzzy set, an element can have any value between 0 and 1, indicating its degree of membership.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: In a fuzzy set for "tall people," someone who is 5'10" might have a membership degree of 0.7, while someone who is 6'2" might have a membership degree of 0.9.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Linguistic variables&lt;/strong&gt; : These are variables whose values are words or sentences rather than numbers. They allow us to express complex concepts in natural language terms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Temperature could be a linguistic variable with values like "cold," "cool," "warm," and "hot."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fuzzy rules&lt;/strong&gt; : These are conditional statements that use linguistic variables, typically in an IF-THEN format. They form the basis of fuzzy reasoning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: "IF temperature is hot AND humidity is high THEN comfort is low"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fuzzification&lt;/strong&gt; : This is the process of converting crisp (precise) input values into fuzzy values. It involves determining the degree to which these inputs belong to each of the appropriate fuzzy sets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Converting a precise temperature of 28°C into fuzzy values like "0.7 warm" and "0.3 hot"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inference&lt;/strong&gt; : This step applies the fuzzy rules to the fuzzified inputs to determine the fuzzy output. It involves evaluating all rules in parallel and combining their results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Applying multiple rules about temperature, humidity, and wind to determine the overall comfort level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Defuzzification&lt;/strong&gt; : This is the final step where the fuzzy output is converted back into a crisp (precise) value. There are several methods for this, such as the centroid method or mean of maximum.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Converting a fuzzy comfort level (e.g., "0.6 uncomfortable") into a specific value on a scale of 1-10&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo time: Fan Control System
&lt;/h2&gt;

&lt;p&gt;Let's create a fuzzy logic system to control a fan based on temperature and humidity. The system will adjust the fan speed (low, medium, high) depending on the fuzzy input values for temperature and humidity.&lt;/p&gt;

&lt;p&gt;Create a new file with the extension &lt;code&gt;.clp&lt;/code&gt; and open it with any code editor that you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Definition of Membership functions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(deftemplate Temperature
  0 40
  ((cold (0 1) (10 0))
   (warm (5 0) (20 1) (30 0))
   (hot (25 0) (40 1) (40 1))))

(deftemplate Humidity
  0 100
  ((low (0 1) (20 0))
   (medium (10 0) (50 1) (90 0))
   (high (70 0) (100 1) (100 1))))

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

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;Temperature&lt;/code&gt; and &lt;code&gt;Humidity&lt;/code&gt; are the fuzzy variables with membership functions &lt;code&gt;cold&lt;/code&gt;, &lt;code&gt;warm&lt;/code&gt;, &lt;code&gt;hot&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, and &lt;code&gt;high&lt;/code&gt;. These are going to be our entry points for the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(deftemplate FanSpeed
  0 100
  ((slow (0 1) (30 0))
   (medium (20 0) (50 1) (80 0))
   (fast (60 0) (100 1) (100 1))))

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

&lt;/div&gt;



&lt;p&gt;Lastly let's add the &lt;code&gt;FanSpeed&lt;/code&gt; with &lt;code&gt;slow&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, and &lt;code&gt;fast&lt;/code&gt; functions, which are going to get activated based on the rules of the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Definition of rules
&lt;/h3&gt;

&lt;p&gt;Create rules to determine the fan speed based on temperature and humidity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(defrule cool-fan
  (Temperature cold)
  (Humidity low)
 =&amp;gt;
  (assert (FanSpeed slow)))

(defrule moderate-fan
  (Temperature warm)
  (Humidity medium)
 =&amp;gt;
  (assert (FanSpeed medium)))

(defrule hot-humid-fan
  (Temperature hot)
  (Humidity high)
 =&amp;gt;
  (assert (FanSpeed fast)))

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

&lt;/div&gt;



&lt;p&gt;These rules set the fan speed to &lt;code&gt;slow&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, or &lt;code&gt;fast&lt;/code&gt; based on the combination of temperature and humidity.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Definition of Facts
&lt;/h3&gt;

&lt;p&gt;Provide sample data for temperature and humidity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;;; Sample facts for temperature and humidity
(deffacts sample-facts
  (Temperature (25 0) (25 1) (25 0))
  (Humidity (40 0) (40 1) (40 0)))

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

&lt;/div&gt;



&lt;p&gt;These facts represent a temperature of 25°C and a humidity of 40%.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Running the system
&lt;/h3&gt;

&lt;p&gt;Open a new terminal in your system and execute the program &lt;code&gt;fz_clips&lt;/code&gt; - the one that we compiled in the beginning. Once running, we need to load our program into the library. For this, we need to use the &lt;code&gt;load&lt;/code&gt; function and send the path of the file containing the rules we defined previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (load ./demo.clp)
Defining deftemplate: Temperature
Defining deftemplate: Humidity
Defining deftemplate: FanSpeed
Defining defrule: cool-fan +j+j
Defining defrule: moderate-fan +j+j
Defining defrule: hot-humid-fan +j+j
Defining deffacts: sample-facts
TRUE

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

&lt;/div&gt;



&lt;p&gt;Now we need to initialize the environment, to load all the facts and rules into the memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (reset)

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

&lt;/div&gt;



&lt;p&gt;We can double-check if everything was loaded correctly with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (facts)
f-0 (initial-fact) CF 1.00
f-1 (Temperature ???) CF 1.00
        ( (25.0 0.0) (25.0 1.0) (25.0 0.0) )

f-2 (Humidity ???) CF 1.00
        ( (40.0 0.0) (40.0 1.0) (40.0 0.0) )

For a total of 3 facts.


FuzzyCLIPS&amp;gt; (rules)
cool-fan
moderate-fan
hot-humid-fan
For a total of 3 defrules.

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

&lt;/div&gt;



&lt;p&gt;Now it's time to execute the rules based on the asserted facts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (run)

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

&lt;/div&gt;



&lt;p&gt;Alternatively, we can make partial execution by specifying the number of steps, for example, rule by rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (run 1)

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

&lt;/div&gt;



&lt;p&gt;At any point, we always check our facts to see the status of our system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (facts)
f-0 (initial-fact) CF 1.00
f-1 (Temperature ???) CF 1.00
        ( (25.0 0.0) (25.0 1.0) (25.0 0.0) )

f-2 (Humidity ???) CF 1.00
        ( (40.0 0.0) (40.0 1.0) (40.0 0.0) )

f-3 (FanSpeed ???) CF 1.00
        ( (20.0 0.0) (35.0 0.5) (65.0 0.5) (80.0 0.0) )

For a total of 4 facts.

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

&lt;/div&gt;



&lt;p&gt;As we can see the fact of &lt;code&gt;FanSpeed&lt;/code&gt; has been loaded and some values set. Fuzzy clips provides a way to &lt;strong&gt;visualize&lt;/strong&gt; it in the terminal by using the function &lt;code&gt;plot-fuzzy-value&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (plot-fuzzy-value t + 0 100 3)

Fuzzy Value: FanSpeed
Linguistic Value: ??? (+)

 1.00
 0.95
 0.90
 0.85
 0.80
 0.75
 0.70
 0.65
 0.60
 0.55
 0.50 +++++++++++++++
 0.45 + +
 0.40 + +
 0.35 + +
 0.30
 0.25 + +
 0.20 + +
 0.15 + +
 0.10
 0.05 + +
 0.00+++++++++++ +++++++++++
     |----|----|----|----|----|----|----|----|----|----|
    0.00 20.00 40.00 60.00 80.00 100.00

Universe of Discourse: From 0.00 to 100.00

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

&lt;/div&gt;



&lt;p&gt;Finally, we can perform a defuzzification process on any fact to see its &lt;strong&gt;crisp&lt;/strong&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FuzzyCLIPS&amp;gt; (moment-defuzzify 3)
50.0

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fun fact: Clips is compatible with Clojure syntax
&lt;/h2&gt;

&lt;p&gt;In case want to keep a strict formatting in your code, I have good news for you! You can change your syntax language to &lt;a href="https://clojure.org/" rel="noopener noreferrer"&gt;Clojure&lt;/a&gt; and it will format your code accordingly. The main reason is because Clojure tries to keep the same formatting practices as in LISP, therefore it's &lt;em&gt;compatbile&lt;/em&gt; with CLIPS as well!&lt;/p&gt;

&lt;p&gt;You can easily install any formatter in your code editor and on save you can pass from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(deftemplate Humidity
    0 100
    (
        (low (0 1) (20 0))
        (medium (10 0) (50 1) (90 0))
        (high (70 0) (100 1) (100 1))
    )
)

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

&lt;/div&gt;



&lt;p&gt;To this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(deftemplate Humidity
  0 100
  ((low (0 1) (20 0))
   (medium (10 0) (50 1) (90 0))
   (high (70 0) (100 1) (100 1))))

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

&lt;/div&gt;



&lt;p&gt;Which one is better? I actually don't care so much, what I care is all my code is now formatted in the same way and I don't have to manually start adding spacing.&lt;/p&gt;

&lt;p&gt;Another tip, you can use the formatter as some sort of &lt;em&gt;compiler&lt;/em&gt; mostly to check if you skip some parenthesis while you were writing. As you can see from the previous example, there quite a lot of them and it can quite easy to forget to close one of them. The formatter will fail in case you have some parenthesis that are not properly closed, which means that you can speed up your process because you don't have to load and execute your program in Fuzzy CLIPS to see the error message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;As I mentioned at the beginning of this article, the amount of information about this framework is quite scarce and it took me quite some time to gather all this information. Hopefully, more people will find it handy as well!&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Adding support for LaTeX in Astro.js</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Mon, 24 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/adding-support-for-latex-in-astrojs-98j</link>
      <guid>https://dev.to/emasuriano/adding-support-for-latex-in-astrojs-98j</guid>
      <description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/LaTeX" rel="noopener noreferrer"&gt;LaTeX&lt;/a&gt; can be considered the standard language to represent mathematical expression in any possible field, and adding it to your page content is not that complicated. In case you already have an Astro project, and you are generating your pages from Markdown files, then you can easily add support by extending your &lt;a href="https://docs.astro.build/en/guides/markdown-content/#markdown-plugins" rel="noopener noreferrer"&gt;Markdown plugins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The great open source project &lt;a href="https://github.com/remarkjs/remark-math/tree/main?tab=readme-ov-file" rel="noopener noreferrer"&gt;remark-math&lt;/a&gt; allows you to extend your markdown and be able to use the classic &lt;code&gt;$$&lt;/code&gt; or &lt;code&gt;$&lt;/code&gt; and transform them into LaTeX syntax on the spot. The first step is to install both &lt;code&gt;remark-math&lt;/code&gt; and one of the math rendering extensions: &lt;code&gt;rehype-katex&lt;/code&gt; (uses &lt;a href="https://katex.org/" rel="noopener noreferrer"&gt;KaTeX&lt;/a&gt;) or &lt;code&gt;rehype-mathjax&lt;/code&gt;(uses &lt;a href="https://www.mathjax.org/" rel="noopener noreferrer"&gt;MathJax&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I personally prefer &lt;code&gt;rehype-katex&lt;/code&gt; mostly because the LaTeX text can be selected in the HTML and copied to the clipboard, where &lt;code&gt;rehype-mathjax&lt;/code&gt; simply generates an &lt;code&gt;svg&lt;/code&gt; tag that doesn't allow selection.&lt;/p&gt;

&lt;p&gt;The next step is to add both extensions to your &lt;code&gt;astro.config.mjs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineConfig } from "astro/config";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";

export default defineConfig({
  ...restConfig,
  markdown: {
    syntaxHighlight: "prism",
    remarkPlugins: [remarkMath],
    rehypePlugins: [rehypeKatex],
  },
});

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

&lt;/div&gt;



&lt;p&gt;In case you also decided to use &lt;code&gt;rehype-katex&lt;/code&gt; you have to add the following &lt;code&gt;link&lt;/code&gt; tag to the &lt;code&gt;head&lt;/code&gt; of your page, to load additional styling that KaTeX needs to render properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- Katex --&amp;gt;
&amp;lt;link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css"
  integrity="sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ"
  crossorigin="anonymous"
/&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;The setup of the extensions is now done and you can represent LaTeX by using the &lt;code&gt;$&lt;/code&gt; symbol. Some examples are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Inline expression: $f(x) = x + 1$

- New line + centered expression:
  $$
  f(x) = x + 1
  $$

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

&lt;/div&gt;



&lt;p&gt;Which looks the following way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline expression: $f(x) = x + 1$&lt;/li&gt;
&lt;li&gt;New line + centered expression: $$ f(x) = x + 1 $$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all for now, thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Simplifying project navigation with git-open</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Wed, 19 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/simplifying-project-navigation-with-git-open-52cf</link>
      <guid>https://dev.to/emasuriano/simplifying-project-navigation-with-git-open-52cf</guid>
      <description>&lt;p&gt;If you are the kind of person that works in different projects at the same time and have a folder of bookmarks with the links to the repositories, then &lt;code&gt;git-open&lt;/code&gt; is the CLI for you!&lt;/p&gt;

&lt;p&gt;Once installed, the only thing you have to do is run &lt;code&gt;git open&lt;/code&gt; from your project folder, and you will see a new browser tab open with the repository linked to it (it takes this information from the &lt;code&gt;origin&lt;/code&gt;) with even the branch you are currently working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5nht2hutjjgn168y043r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5nht2hutjjgn168y043r.gif" alt="Demo" width="640" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To install it simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g git-open

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

&lt;/div&gt;



&lt;p&gt;Because it's installed globally, you can access the tool from any project you are working on. Once you get used to the command, it becomes quite natural to use it to have quick access to PRs or issues.&lt;/p&gt;

&lt;p&gt;Oh, it works with many providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com" rel="noopener noreferrer"&gt;github.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gist.github.com" rel="noopener noreferrer"&gt;gist.github.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gitlab.com" rel="noopener noreferrer"&gt;gitlab.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bitbucket.org" rel="noopener noreferrer"&gt;bitbucket.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the official repository in GitHub at: &lt;a href="https://github.com/paulirish/git-open" rel="noopener noreferrer"&gt;https://github.com/paulirish/git-open&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Run a Linux Distro in your Android device</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Thu, 11 Apr 2024 00:16:49 +0000</pubDate>
      <link>https://dev.to/emasuriano/run-a-linux-distro-in-your-android-device-3fnd</link>
      <guid>https://dev.to/emasuriano/run-a-linux-distro-in-your-android-device-3fnd</guid>
      <description>&lt;p&gt;This post is going to be about how I set up an Android tablet to be able to run a Linux Distro in order to have a desktop experience and boost the productivity due to the Window Manager.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; The whole process happens locally in the tablet, therefore all the programs are executed inside the tablet. I saw some other tutorials where people use &lt;a href="https://github.com/coder/code-server" rel="noopener noreferrer"&gt;code-server&lt;/a&gt; and besides the coding experience might look the same, running the full OS offers more capabilities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is how the end result looks like:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  How I got here?
&lt;/h2&gt;

&lt;p&gt;While I was travelling and reading some Machine Learning books, I wanted to try some of the new things that I was reading, but sadly I didn't have any laptop with me, and I was not planning in getting a new one. The only big screen that I had with me was a 10" Tablet, which I use for streaming and reading.&lt;/p&gt;

&lt;p&gt;The tablet is a &lt;a href="https://www.gsmarena.com/xiaomi_redmi_pad_se-12466.php" rel="noopener noreferrer"&gt;Redmi Pad SE&lt;/a&gt; running Android 14 with 8 GB of RAM, Snapdragon 680 for the CPU and 256 GB of disk. It's not a bad tablet at all, but clearly is not the best one, specially the CPU could have been better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s2b2s8dr886wd77gjry.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s2b2s8dr886wd77gjry.jpg" alt="Tablet specs" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only piece of Hardware that I would highly recommend getting is a Bluetooth keyboard. Using the virtual keyboard in the screen takes quite a lot of space in the screen and highly reduce the productivity while using the desktop. I got a &lt;a href="https://www.logitech.com/en-us/products/keyboards/k380-multi-device.920-009600.html" rel="noopener noreferrer"&gt;Logitech K380&lt;/a&gt;, which is one of the cheapest and most compact Bluetooth keyboard that you can find stores.&lt;/p&gt;

&lt;p&gt;One valid argument can be: what's the point of running Linux in a tablet if I already have a computer/laptop for development? I came up with the following points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Convenience&lt;/em&gt;: In case you don't have computer around, then with this setup you &lt;strong&gt;can do basically the same stuff&lt;/strong&gt;. In my case, I tried cloning repositories, installing dependencies, running node server, opening browser and inspecting the website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Affordability&lt;/em&gt;: If you don't have a computer, this setup is &lt;strong&gt;way cheaper&lt;/strong&gt; than buying a new laptop or even worse the whole setup for a desktop computer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Portability&lt;/em&gt;: This point might depend on your Hardware, in my case the combo of Tablet + Keyboard is &lt;strong&gt;lighter&lt;/strong&gt; than a laptop. Also considering that you can use the same charger that you use for your phone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;The only things applications that you need to download are:&lt;/p&gt;

&lt;p&gt;- &lt;a href="https://termux.dev/en/" rel="noopener noreferrer"&gt;Termux&lt;/a&gt;: terminal application that allows to emulate bash and have access to package installation like &lt;code&gt;apt install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;- &lt;a href="https://github.com/termux/termux-x11" rel="noopener noreferrer"&gt;Termux:X11&lt;/a&gt;: plugin for Termux to connect to instance of servers and handle the connection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important disclaimer&lt;/strong&gt;: You can find the latest version of Termux inside F-Droid instead of Play Store, so please download those instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to keep this post mostly practical, therefore in case you want to read how the actual desktop emulation run, please check out the &lt;a href="https://github.com/termux/termux-x11/blob/master/README.md" rel="noopener noreferrer"&gt;official repository of the plugin&lt;/a&gt;. It provides an in depth description of the inner commands and options available inside the plugin.&lt;/p&gt;

&lt;p&gt;Once both applications are installed, we can start with the installation of the project dependencies and image of the OS. For this step I found a &lt;a href="https://github.com/phoenixbyrd/Termux_XFCE" rel="noopener noreferrer"&gt;great GitHub repository&lt;/a&gt; where the whole process is automatized, so I want to send great kudos to &lt;a href="https://github.com/phoenixbyrd" rel="noopener noreferrer"&gt;@phoenixbyrd&lt;/a&gt; and rest of the team which made the installation script. Inside his README you can find:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sets up a Termux XFCE desktop and a Debian proot install. This setup uses Termux-X11, the termux-x11 server will be installed, and you will be prompted to allow Termux to install the Android APK.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also consider that you are going to need at least 4 GB of storage space for the installation and as you install applications, they will take even more.&lt;/p&gt;

&lt;p&gt;Open Termux and paste 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;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://raw.githubusercontent.com/phoenixbyrd/Termux_XFCE/main/setup.sh &lt;span class="nt"&gt;-o&lt;/span&gt; setup.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x setup.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./setup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might take some time because it has to download all the resources + packages for the OS. In case you encountered some issue during the installation, you can&lt;/p&gt;

&lt;p&gt;join their &lt;a href="https://discord.gg/pNMVrZu5dm" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; or check out the &lt;a href="https://github.com/phoenixbyrd/Termux_XFCE/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt; of the repository.&lt;/p&gt;

&lt;p&gt;Assuming your installation is successful, the setup already added a new &lt;code&gt;start&lt;/code&gt; command into your Termux that allows you start the server and at the same time boot Termux:X11 with the same port.&lt;/p&gt;

&lt;p&gt;I tested several setup in the past using approaches like &lt;a href="https://andronix.app" rel="noopener noreferrer"&gt;Andronix&lt;/a&gt; and this setup is better in many ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Installation&lt;/em&gt;: the setup script takes care of downloading the resources you need and a single place for troubleshooting. This is extremely helpful because there are so many parts that can be misconfigured and searching in Stack Overflow is not that helpful due to the configuration of someone might defer from yours.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Smoother experience&lt;/em&gt;: Termux:X11 provides a smoother experience where the response time is faster to other alternatives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;One button to start&lt;/em&gt;: simply type &lt;code&gt;start&lt;/code&gt; in Termux and in less than 5 seconds you are inside your desktop. No need to configure IP or have both application running, the command does everything for us.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuration of the Desktop
&lt;/h2&gt;

&lt;p&gt;At this point you can do pretty much whatever you want with the desktop like installation packages or applications. Nevertheless, that you are not running a real Unix Kernel and some modules might be missing, that's why not all the applications can be installed directly.&lt;/p&gt;

&lt;p&gt;Luckily this project already have the solution for that! In your desktop there is an app called "App Installer". Once opened you can find many applications that are "modded" so they can run in this kind of virtual environment.&lt;/p&gt;

&lt;p&gt;Some good examples are Visual Studio Code (even Extensions works!), Notion, Brave browser, PyCharm and many more! Just select the one you want to install and click on OK.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Small caveat&lt;/em&gt;: there is no installation progression indicator, so after selecting the application to install it might seem that nothing is happening, but simply wait a couple of minutes, and you should see a new alert saying that the App has been installed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Demo time
&lt;/h2&gt;

&lt;p&gt;Honestly anything can be a proof that the setup works well, because at this point you can browse the internet, run bash scripts or even play desktop games. In my case, I use it mostly for web development, so that's what I'm going to showcase.&lt;/p&gt;

&lt;p&gt;The first step is to install the IDE to handle files and changes. Nowadays, VS Code is the go-to editor for web development, which can be easily installed via the "App installer" as I described before.&lt;/p&gt;

&lt;p&gt;Once installed we can open it and select the option to clone a new repository. I use GitHub to store my public repositories, which VS Code has an integration already built in for it.&lt;/p&gt;

&lt;p&gt;Depending on the stack of the repository you are cloning, you might have to install additional dependencies. For this demo, I'm using my &lt;a href="https://emasuriano.com" rel="noopener noreferrer"&gt;own website&lt;/a&gt;, which is a static website built with &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro.js&lt;/a&gt;. It which requires to have &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed and &lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;Yarn&lt;/a&gt; for package manager.&lt;/p&gt;

&lt;p&gt;Once all the dependencies are installed, we can now start the server. In my case, I run &lt;code&gt;yarn start&lt;/code&gt; which starts a local server that can be opened in the browser.&lt;/p&gt;

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

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

&lt;p&gt;For Web Development one of the most powerful tools is the Dev Tools, where you can inspect DOM Nodes, network activity or even trigger commands from the terminal. This kind of tooling is normally missing in Android and thanks to this setup we can have it.&lt;/p&gt;

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

&lt;p&gt;Thanks that we are running the local server, can we modify the codebase and see the changes in real time inside our browser. In this case, I modify the name and the company.&lt;/p&gt;

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

&lt;p&gt;Once we are happy with the changes, we can commit our changes to saved our changes and potentially trigger a new deployment. With this we basically cover how we can do basic web development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;I normally don't write this kind of posts of "tech hacks" but I had so much fun tweaking the tablet that I just wanted to make a post documenting the process to help others. On top of that, when I was re-searching for this kind of stuff I would have wished to find some kind of post like this one!&lt;/p&gt;

&lt;p&gt;Feel free to reach out to me or leave a comment in this post, I can try to help with the process or even better you consider joining the &lt;a href="https://discord.gg/pNMVrZu5dm" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; where the real maintainers of the project can help you.&lt;/p&gt;

&lt;p&gt;Have fun hacking, and thanks for reading.&lt;/p&gt;

</description>
      <category>android</category>
      <category>linux</category>
      <category>termux</category>
      <category>x11</category>
    </item>
    <item>
      <title>Keep your Documentation updated with Cypress and GitHub Actions</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Tue, 03 Nov 2020 10:01:18 +0000</pubDate>
      <link>https://dev.to/emasuriano/keep-your-documentation-updated-with-cypress-and-github-actions-15di</link>
      <guid>https://dev.to/emasuriano/keep-your-documentation-updated-with-cypress-and-github-actions-15di</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@emasuriano/keep-your-documentation-updated-with-cypress-and-github-actions-f3bdafcd7ffb?source=rss-cc2fb20f639d------2" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uE3OHt6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2A_FEzyLrmlvpPKzNV" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of my favorite ways to document projects is by adding screenshots of how it looks, to provide a quick overview of what it does and looks like. Sadly these images are quite easy to get out-dated, and I was being forced to manually update them ... In this post, how I automatize this task by using Cypress and GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@emasuriano/keep-your-documentation-updated-with-cypress-and-github-actions-f3bdafcd7ffb?source=rss-cc2fb20f639d------2" rel="noopener noreferrer"&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>automation</category>
      <category>workflow</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Keep your Documentation updated with Cypress and Github Actions</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Mon, 26 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/keep-your-documentation-updated-with-cypress-and-github-actions-593j</link>
      <guid>https://dev.to/emasuriano/keep-your-documentation-updated-with-cypress-and-github-actions-593j</guid>
      <description>&lt;p&gt;One of my favorite ways to document projects is by adding screenshots of how it looks, to provide a quick overview of what it does and looks like. Sadly these images are quite easy to get out-dated, and I was being forced to manually update them ... In this post, how I automatize this task by using Cypress and GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Case in Particular
&lt;/h2&gt;

&lt;p&gt;I am the owner and maintainer of the &lt;a href="https://github.com/EmaSuriano/gatsby-starter-mate" rel="noopener noreferrer"&gt;Gatsby Starter Mate&lt;/a&gt;, which allows developers or tech writers to bootstrap their portfolio and manage its content with a CMS.&lt;/p&gt;

&lt;p&gt;Although one can always open the &lt;a href="https://gatsby-starter-mate.netlify.app/" rel="noopener noreferrer"&gt;Demo website&lt;/a&gt; and navigate through it to see how the project is, I decided to create a simple Table where I show how each of the sections looks like. Here is the table extracted from the &lt;a href="https://github.com/EmaSuriano/gatsby-starter-mate/blob/master/README.md#screenshot-and-design-" rel="noopener noreferrer"&gt;README.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4krja398giam0fpvw8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4krja398giam0fpvw8k.png" alt="Home" width="800" height="450"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31gr88gqampog6vemwot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31gr88gqampog6vemwot.png" alt="About me" width="800" height="503"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuaig8rg4pveesoxrnu61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuaig8rg4pveesoxrnu61.png" alt="Projects" width="800" height="450"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8qxybjzj3qprggwguxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8qxybjzj3qprggwguxy.png" alt="Writing" width="800" height="450"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcizc2pymd5ulq2b3v3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcizc2pymd5ulq2b3v3k.png" alt="404" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I highlighted at the beginning of this post, in case I made a change in the UI: CSS change, content change, add/remove new components, etc. I had to regenerate these screenshots ... The process was as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the development server.&lt;/li&gt;
&lt;li&gt;Open the website in the browser (trying to always use the same window size).&lt;/li&gt;
&lt;li&gt;Take a screenshot and store it in the repository.&lt;/li&gt;
&lt;li&gt;Remove the old image and rename the new with the original name.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Besides the process was not hard to follow, I had to repeat it for each of the sections and in many times I forgot to do it after I made any change ... It was time to bring the machines to the play! 🦾&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Cypress 🤖
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; is a JavaScript End-to-End Testing that provides a nice framework to work with and it's capable of emulating a browser that can interact with any website by using the browser APIs.&lt;/p&gt;

&lt;p&gt;Even though this tool was built in mind of Testing you can use it as a &lt;em&gt;Photographer&lt;/em&gt;, to make the Browser to take screenshots of your website and store them on your repository!&lt;/p&gt;

&lt;p&gt;As this tool is not needed when the application is running in production, it's always recommended installing it as a development dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# npm
&amp;gt; npm install cypress --save-dev

# yarn
&amp;gt; yarn add cypress --dev

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

&lt;/div&gt;



&lt;p&gt;Then you have to create a file called &lt;code&gt;cypress.json&lt;/code&gt; located at the root of your directory, providing the URL of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "baseUrl": "http://localhost:3000/",
  "screenshotsFolder": "screenshots"
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;DISCLAIMER:&lt;/strong&gt; In case you provided a &lt;code&gt;localhost&lt;/code&gt; route, you have to ensure that your development server is running in the background when Cypress is running.&lt;/p&gt;

&lt;p&gt;Next, let's add a simple health check that will navigate to the Home Page of the project and check if the local server is answering correctly. For that create a file called &lt;code&gt;health.spec.js&lt;/code&gt;, located inside a folder called &lt;code&gt;cypress/integration&lt;/code&gt; at the root folder of the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// cypress/integration/health.spec.js

it('health test', () =&amp;gt; {
  cy.visit('/');
});

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

&lt;/div&gt;



&lt;p&gt;After this you can finally execute Cypress by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; yarn cypress run

====================================================================

  (Run Starting)

  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Cypress: 5.4.0 │
  │ Browser: Electron 85 (headless) │
  │ Specs: 1 found (health.spec.js) │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘

────────────────────────────────────────────────────────────────────────────────────────────────────

  Running: health.spec.js (1 of 1)

  ✓ health test (293ms)

  1 passing (304ms)

  (Results)

  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Tests: 1 │
  │ Passing: 1 │
  │ Failing: 0 │
  │ Pending: 0 │
  │ Skipped: 0 │
  │ Screenshots: 0 │
  │ Video: false │
  │ Duration: 0 seconds │
  │ Spec Ran: health.spec.js │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘

====================================================================

  (Run Finished)

       Spec Tests Passing Failing Pending Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔ health.spec.js 301ms 1 1 - - - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔ All specs passed! 301ms 1 1 - - -

✨ Done in 9.67s.

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

&lt;/div&gt;



&lt;p&gt;This command will create the folders of &lt;code&gt;plugins&lt;/code&gt; and &lt;code&gt;support&lt;/code&gt; inside the Cypress folder with some boilerplate inside, which will come in handy for the future steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking screenshots with Cypress 📸
&lt;/h2&gt;

&lt;p&gt;Time to make Cypress take those screenshots! Let's start by adding a new file inside &lt;code&gt;integration&lt;/code&gt; called &lt;code&gt;photographer.test.js&lt;/code&gt;, with some basic tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('Photographer', () =&amp;gt; {
  beforeEach(() =&amp;gt; {
    cy.viewport('macbook-15'); // Desktop viewport
  });

  // Taking screenshots by Sections
  it('Landing Section', () =&amp;gt; {
    cy.visit('/');
    cy.get('#home').screenshot('Landing');
  });

  it('About Section', () =&amp;gt; {
    cy.visit('/');
    cy.get('#about').scrollIntoView().screenshot('About');
  });

  // Taking screenshots of the whole page
  it('404 Page', () =&amp;gt; {
    cy.visit('/');
    cy.get('#404').screenshot('404');
  });
});

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

&lt;/div&gt;



&lt;p&gt;The URL of the pages to visit and CSS selectors may change depending on your project, but the idea will remain the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visit a particular route of your application.&lt;/li&gt;
&lt;li&gt;Use a CSS selector to pick a section or grab all the content.&lt;/li&gt;
&lt;li&gt;Take a screenshot and save it using a descriptive name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you are done with setting the routes and name of your section, run Cypress by executing &lt;code&gt;yarn cypress run&lt;/code&gt;, and you will see a new folder called &lt;code&gt;screenshots&lt;/code&gt; located at the root of your project with the screenshots taken by Cypress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📦screenshots
 ┗ 📂photographer.test.js
   ┣ 🖼404.png
   ┣ 🖼About.png
   ┣ 🖼Landing.png
   ┣ 🖼Projects.png
   ┗ 🖼Writing.png

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

&lt;/div&gt;



&lt;p&gt;Be aware that Cypress will remove and recreate this folder on every run, so don't worry if your images disappear between runs. Also, at this point you can remove the test &lt;code&gt;health.spec.js&lt;/code&gt;, because it's not needed anymore 👍&lt;/p&gt;

&lt;p&gt;The last piece will be changing your documentation to point to the auto generated pictures instead of the manual. I recommend using a table because it provides a very clear interface for the readers to visualize the pictures. This is the one I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Screenshot and Design 🖼

| Section | Screenshot |
| -------- | :--------------------------------------------------------: |
| Home | ![Home](screenshots/photographer.test.js/Landing.png) |
| About me | ![About me](screenshots/photographer.test.js/About.png) |
| Projects | ![Projects](screenshots/photographer.test.js/Projects.png) |
| Writing | ![Writing](screenshots/photographer.test.js/Writing.png) |
| 404 | ![404](screenshots/photographer.test.js/404.png) |

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automatizing the screenshot (now for real)
&lt;/h2&gt;

&lt;p&gt;Up to this point, Cypress is taking the screenshots for me, but I still have the task to call it by running &lt;code&gt;yarn cypress run&lt;/code&gt;, and also I have to make sure my development server is running at that time to generate valid screenshots.&lt;/p&gt;

&lt;p&gt;To address the latter, I recommend using an npm package called &lt;code&gt;start-server-and-test&lt;/code&gt; which does exactly what is says: start any server, wait for a specific port to be open, and finally run the test. It's very useful in these situations 🙌&lt;/p&gt;

&lt;p&gt;To install it, you can use either yarn or npm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# npm
&amp;gt; npm install start-server-and-test --save-dev

# yarn
&amp;gt; yarn add start-server-and-test --dev

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

&lt;/div&gt;



&lt;p&gt;Then inside your &lt;code&gt;package.json&lt;/code&gt;, you can add the following commands inside the &lt;code&gt;scripts&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "scripts": {
    "test": "cypress run",
    "start": "react-scripts start",
    "take-screenshots": "start-server-and-test 3000"
  }
}

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

&lt;/div&gt;



&lt;p&gt;To test it, you can simply execute the &lt;code&gt;take-screenshot&lt;/code&gt; command and you should be able to see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ my-project git:(master) ✗ yarn take-screenshots
yarn run v1.22.5
$ start-server-and-test 3000
1: starting server using command "npm run start"
and when url "['http://localhost:3000']" is responding with HTTP status code 200
running tests using command "npm run test"

...

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

&lt;/div&gt;



&lt;p&gt;Now I only need something that will call this command automatically for me and push it into my Repository. This is when GitHub Actions comes into the Game!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER:&lt;/strong&gt; you can use any Continuous Integration technology (Travis CI, Circle CI, etc.), but I find the implementation with Actions quite simple and straight forward.&lt;/p&gt;

&lt;p&gt;To enable GitHub Actions, you have to create a new &lt;em&gt;Workflow&lt;/em&gt; file located inside &lt;code&gt;.github/workflows&lt;/code&gt; folder. You can name this file as you want, the only rule to follow is that it has to be a YAML file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .github/workflows/update-docs.yml

# Workflow name
name: Update Docs

# Run on every push to master
on:
  push:
    branches: master

jobs:
  # Job name
  update-readme:
    # Environment setup
    runs-on: ubuntu-latest

    # job
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Setup Node
        uses: actions/setup-node@v1
      - name: Install dependencies
        run: yarn install --frozen-lockfile
      - name: Take screenshots
        run: yarn take-screenshots
      - name: Commit screenshots
        uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: Update Screenshots

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

&lt;/div&gt;



&lt;p&gt;Something I like about the Workflows from GitHub Actions is how descriptive they are. It consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name of the workflow, which serves as an identifier (&lt;code&gt;name&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;When the Workflow will be triggered (&lt;code&gt;on&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;What it will do once triggered (&lt;code&gt;jobs&lt;/code&gt;). Each Job has:

&lt;ul&gt;
&lt;li&gt;Unique identifier.&lt;/li&gt;
&lt;li&gt;On which environment it will run (&lt;code&gt;runs-on&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Set of steps to run scripts or call other Actions (&lt;code&gt;steps&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;One of the best features of GitHub Actions is the ability to compose Actions. In the example from above, to commit the changes inside the screenshots I am using the &lt;code&gt;git-auto-commit-action&lt;/code&gt; which will look at my changes and then perform a &lt;code&gt;git commit -am "commit_message"&lt;/code&gt; into my repository.&lt;/p&gt;

&lt;p&gt;Finally, to test if your Workflow is working you have to push your changes into your &lt;code&gt;master&lt;/code&gt; branch. Here you can see a normal execution I run inside &lt;a href="https://github.com/EmaSuriano/gatsby-starter-mate/runs/1309156962" rel="noopener noreferrer"&gt;gatsby-starter-mate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it to the next level 🚀
&lt;/h2&gt;

&lt;p&gt;In case you have noticed inside the &lt;code&gt;photographer.test.js&lt;/code&gt; file, I had to type the name of my routes and the selectors that I wanted to use. But what if, I can also automate this too? 🤔&lt;/p&gt;

&lt;p&gt;The first step will be to collect all the routes inside the application. This will change depending on the technology and routing you have picked. But from here you should produce a file called &lt;code&gt;routes.json&lt;/code&gt; with the list of all the routes.&lt;/p&gt;

&lt;p&gt;Next, you have to store this file inside the folder &lt;code&gt;cypress/fixtures&lt;/code&gt;, so then Cypress can have access to it inside the &lt;code&gt;photographer.test.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('Dynamic Photographer', () =&amp;gt; {
  const routes = require('../fixtures/routes.json');

  beforeEach(() =&amp;gt; {
    cy.viewport('macbook-15'); // Desktop viewport
  });

  routes.forEach((route) =&amp;gt; {
    it(`${route}`, () =&amp;gt; {
      cy.visit('/');
      cy.get('#shared-section-id').screenshot(route);
    });
  });
});

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

&lt;/div&gt;



&lt;p&gt;Finally, you have to generate the table based on the &lt;code&gt;routes.json&lt;/code&gt; file or from the generated images. In this case, I will do it from the file, but in both cases, the output should be the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// generate-docs-readme.js
const fs = require('fs');
const routes = require('./cypress/fixtures/routes.json');

const routeToTable = (route) =&amp;gt;
  `| ${route} | ![${route}](screenshots/photographer.test.js/${route}.png) |`;

const content = [
  '## Pages Screenshots',
  'Dynamic screenshots based on the last version deployed.',
  '| Page | Screenshot |',
  '| --- | :---: |',
  ...routes.map(routeToTable),
];

fs.writeFileSync('photographer.md', content.join('\n'));

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

&lt;/div&gt;



&lt;p&gt;This idea can be improved in several ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expose specific CSS selectors depending on the screen, to have more accurate screenshots.&lt;/li&gt;
&lt;li&gt;Expose names of screens instead of treating them as routes.&lt;/li&gt;
&lt;li&gt;With the combination of the idea from above, your file &lt;code&gt;routes.json&lt;/code&gt; can tell Cypress to take more than one picture from a Page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Last Words
&lt;/h2&gt;

&lt;p&gt;I had a ton of fun integrating these two tools and, I think the result it's quite useful. I hope more people also found it appealing and decided to try it on their own projects to battle against out-dated documentation 😅&lt;/p&gt;

&lt;p&gt;In this post, I nearly used all the potential from these two tools. I highly recommend passing by though their official documentation: &lt;a href="https://docs.cypress.io" rel="noopener noreferrer"&gt;Cypress Docs&lt;/a&gt; and &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;GitHub Actions Docs&lt;/a&gt;. They provide much more functionality inside.&lt;/p&gt;

&lt;p&gt;Thanks for reading and let's keep building stuff together! 👷‍♂️&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/EmaSuriano/gatsby-starter-mate" rel="noopener noreferrer"&gt;Gatsby Starter Mate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cypress.io" rel="noopener noreferrer"&gt;Cypress Documentation Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/EmaSuriano/gatsby-starter-mate/runs/1309156962" rel="noopener noreferrer"&gt;Example GitHub Actions in Gatsby Starter Mate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>End-to-end testing in React Native with Detox</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Fri, 29 May 2020 16:24:00 +0000</pubDate>
      <link>https://dev.to/emasuriano/end-to-end-testing-in-react-native-with-detox-30b7</link>
      <guid>https://dev.to/emasuriano/end-to-end-testing-in-react-native-with-detox-30b7</guid>
      <description>&lt;p&gt;Article originally published on the &lt;a href="https://blog.logrocket.com/end-to-end-testing-in-react-native-with-detox/" rel="noopener noreferrer"&gt;LogRocket Blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;End-to-end testing is a technique that is widely performed in the web ecosystem with frameworks like &lt;a href="https://github.com/cypress-io/cypress" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt;, &lt;a href="https://github.com/GoogleChrome/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;, or maybe with your own custom implementation.&lt;/p&gt;

&lt;p&gt;But when it comes to the mobile world, this practice is not that common, and there are several existing solutions to address. I have a theory that most mobile developers think testing mobile applications is hard and requires a lot of setup and configuration, and therefore they just skip it.&lt;/p&gt;

&lt;p&gt;The goal of this article is to explain how to implement the end-to-end testing framework &lt;a href="https://github.com/wix/Detox" rel="noopener noreferrer"&gt;Detox&lt;/a&gt; in a React Native application, write a bunch of interaction tests, and, finally, integrate it into your development workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Disclaimer ️️⚠️:&lt;/strong&gt; Given the length of this article, I will only focus on the iOS flow. Nevertheless, I plan to release a second part covering Android, too.&lt;/p&gt;

&lt;h3&gt;
  
  
  A quick introduction to end-to-end testing 📖
&lt;/h3&gt;

&lt;p&gt;Let’s take a look at the definition of this testing technique:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;End-to-end testing is a technique used to test whether the flow of an application right from start to finish is behaving as expected. The purpose of performing end-to-end testing is to identify system dependencies and to ensure that data integrity is maintained between various system components and systems. — &lt;a href="https://www.tutorialspoint.com/software_testing_dictionary/end_to_end_testing.htm" rel="noopener noreferrer"&gt;Software Testing Dictionary&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In contrast to unit testing, end-to-end testing tries to cover as much of your application’s functionality as it can. The more it covers, the more reliable your tests will be. Therefore, it includes all the stages of an application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up environment&lt;/li&gt;
&lt;li&gt;Installation of the application (if it’s necessary)&lt;/li&gt;
&lt;li&gt;Initialization&lt;/li&gt;
&lt;li&gt;Executing routines&lt;/li&gt;
&lt;li&gt;Expecting events or behaviors to happen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how end-to-end testing looks in the browser using &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&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%2F0%2A8XsPovDkCJtDKRSj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A8XsPovDkCJtDKRSj.gif"&gt;&lt;/a&gt;End-to-End Testing with Cypress&lt;/p&gt;

&lt;p&gt;Cypress is able to create an instance of Chrome, run a URL, and then start interacting with the webpage by selecting elements (&lt;code&gt;div&lt;/code&gt;, &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;) using native selectors (&lt;code&gt;getElementById&lt;/code&gt;, &lt;code&gt;getElementByName&lt;/code&gt;, &lt;code&gt;getElementByClassName&lt;/code&gt;), and then triggering events (&lt;code&gt;click&lt;/code&gt;, &lt;code&gt;change&lt;/code&gt;, &lt;code&gt;focus&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;At any point in the tests, the developer can &lt;code&gt;assert&lt;/code&gt;/&lt;code&gt;expect&lt;/code&gt; something to happen or to have a specific value. In case all the expectations were true, the result of the test suite will be successful.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-end testing in mobile 🤯
&lt;/h3&gt;

&lt;p&gt;The process of testing mobile applications is actually quite similar to the web. Let’s go thought the previously described steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set up the environment:&lt;/strong&gt; create an instance of an emulator (Android/iOS device)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installation:&lt;/strong&gt; install the application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialization:&lt;/strong&gt; run the application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Executing routines:&lt;/strong&gt; Depending on the framework this may change, but all of them are using native directives to get the reference of an element (&lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;View&lt;/code&gt;, &lt;code&gt;TextInput&lt;/code&gt;) and then executing actions (&lt;code&gt;press&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;focus&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expecting events:&lt;/strong&gt; Using the same functions described before, they can validate values or events that happened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how end-to-end testing looks like in mobile using &lt;a href="https://github.com/wix/Detox" rel="noopener noreferrer"&gt;Detox&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%2F799%2F0%2Ay5yL2kwPQ5dzGr3h" 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%2F799%2F0%2Ay5yL2kwPQ5dzGr3h"&gt;&lt;/a&gt;End-to-End Testing with Detox&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Detox, and why should you pick it?
&lt;/h3&gt;

&lt;p&gt;Detox is an end-to-end framework for mobile apps developed by &lt;a href="https://www.wix.engineering/" rel="noopener noreferrer"&gt;Wix&lt;/a&gt;, one of the top contributors inside the React Native community. They also maintain amazing projects such as &lt;code&gt;react-native-navigation&lt;/code&gt; and &lt;code&gt;react-native-ui-lib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What I like about this framework is the great abstraction it provides to select and trigger actions on elements. This is how a normal test looks:&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;Login flow&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;should login successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reloadReactNative&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// getting the reference of an element by ID and expecting to be visible&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Getting the reference and typing&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;typeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john@example.com&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;typeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Getting the reference and executing a tap/press&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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="nf"&gt;tap&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toNotExist&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;As you can see, the syntax is quite readable, and by using async/await, you can write synchronous and easy-to-understand tests. Let’s jump into the demo!&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready, set, code! 🏎
&lt;/h3&gt;

&lt;p&gt;In case you want to skip the explanation and check the code, I’ll give you the &lt;a href="https://github.com/EmaSuriano/e2e-react-native-detox" rel="noopener noreferrer"&gt;link for the repository&lt;/a&gt; with the project already bootstrapped and the tests in place.&lt;/p&gt;

&lt;p&gt;As the focus of this article is testing and not explaining how to set up React Native, I suggest bootstrapping your project using &lt;code&gt;react-native init&lt;/code&gt;, which creates a pretty simple and clean React Native project.&lt;/p&gt;

&lt;p&gt;Start by installing the dependency and creating a fresh new project.&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;react-native &lt;span class="nt"&gt;-g&lt;/span&gt;
~ react-native init testReactNativeDetox

               &lt;span class="c"&gt;######                ######&lt;/span&gt;
             &lt;span class="c"&gt;###     ####        ####     ###&lt;/span&gt;
            &lt;span class="c"&gt;##          ###    ###          ##&lt;/span&gt;
            &lt;span class="c"&gt;##             ####             ##&lt;/span&gt;
            &lt;span class="c"&gt;##             ####             ##&lt;/span&gt;
            &lt;span class="c"&gt;##           ##    ##           ##&lt;/span&gt;
            &lt;span class="c"&gt;##         ###      ###         ##&lt;/span&gt;
             &lt;span class="c"&gt;##  ########################  ##&lt;/span&gt;
          &lt;span class="c"&gt;######    ###            ###    ######&lt;/span&gt;
      &lt;span class="c"&gt;###     ##    ##              ##    ##     ###&lt;/span&gt;
   &lt;span class="c"&gt;###         ## ###      ####      ### ##         ###&lt;/span&gt;
  &lt;span class="c"&gt;##           ####      ########      ####           ##&lt;/span&gt;
 &lt;span class="c"&gt;##             ###     ##########     ###             ##&lt;/span&gt;
  &lt;span class="c"&gt;##           ####      ########      ####           ##&lt;/span&gt;
   &lt;span class="c"&gt;###         ## ###      ####      ### ##         ###&lt;/span&gt;
      &lt;span class="c"&gt;###     ##    ##              ##    ##     ###&lt;/span&gt;
          &lt;span class="c"&gt;######    ###            ###    ######&lt;/span&gt;
             &lt;span class="c"&gt;##  ########################  ##&lt;/span&gt;
            &lt;span class="c"&gt;##         ###      ###         ##&lt;/span&gt;
            &lt;span class="c"&gt;##           ##    ##           ##&lt;/span&gt;
            &lt;span class="c"&gt;##             ####             ##&lt;/span&gt;
            &lt;span class="c"&gt;##             ####             ##&lt;/span&gt;
            &lt;span class="c"&gt;##          ###    ###          ##&lt;/span&gt;
             &lt;span class="c"&gt;###     ####        ####     ###&lt;/span&gt;
               &lt;span class="c"&gt;######                ######&lt;/span&gt;


                  Welcome to React Native!
                 Learn Once Write Anywhere

✔ Downloading template
✔ Copying template
✔ Processing template
✔ Installing dependencies
✔ Installing CocoaPods dependencies &lt;span class="o"&gt;(&lt;/span&gt;this may take a few minutes&lt;span class="o"&gt;)&lt;/span&gt;

  Run instructions &lt;span class="k"&gt;for &lt;/span&gt;iOS:
    • &lt;span class="nb"&gt;cd &lt;/span&gt;testReactNativeDetox &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; react-native run-ios
    - or -
    • Open testReactNativeDetox/ios/testReactNativeDetox.xcworkspace &lt;span class="k"&gt;in &lt;/span&gt;Xcode or run &lt;span class="s2"&gt;"xed -b ios"&lt;/span&gt;
    • Hit the Run button

  Run instructions &lt;span class="k"&gt;for &lt;/span&gt;Android:
    • Have an Android emulator running &lt;span class="o"&gt;(&lt;/span&gt;quickest way to get started&lt;span class="o"&gt;)&lt;/span&gt;, or a device connected.
    • &lt;span class="nb"&gt;cd &lt;/span&gt;testReactNativeDetox &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; react-native run-android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this step, you can try running the application in the emulator by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ &lt;span class="nb"&gt;cd &lt;/span&gt;testReactNativeDetox
~ react-native run-ios
&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AVRK7ICQTATD0WRO3" 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%2F800%2F0%2AVRK7ICQTATD0WRO3"&gt;&lt;/a&gt;&lt;em&gt;Application running&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Time to test! 🔧
&lt;/h3&gt;

&lt;p&gt;Before jumping into testing, you need to have the following &lt;a href="https://github.com/wix/Detox/blob/master/docs/Introduction.GettingStarted.md#prerequisites" rel="noopener noreferrer"&gt;prerequisites&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.apple.com/xcode/" rel="noopener noreferrer"&gt;Xcode&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt; installed and updated&lt;/li&gt;
&lt;li&gt;Node.js installed (&lt;code&gt;brew update &amp;amp;&amp;amp; brew install node&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;applesimutils&lt;/code&gt; installed (&lt;code&gt;brew tap wix/brew; brew install applesimutils;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;detox-cli&lt;/code&gt; installed (&lt;code&gt;npm install -g detox-cli&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start by adding Detox as a dev dependency for the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ yarn add detox &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the CLI, they provide a command that can automatically set up the project. You need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ detox init &lt;span class="nt"&gt;-r&lt;/span&gt; jest

detox[34202] INFO: &lt;span class="o"&gt;[&lt;/span&gt;init.js] Created a file at path: e2e/config.json
detox[34202] INFO: &lt;span class="o"&gt;[&lt;/span&gt;init.js] Created a file at path: e2e/init.js
detox[34202] INFO: &lt;span class="o"&gt;[&lt;/span&gt;init.js] Created a file at path: e2e/firstTest.spec.js
detox[34202] INFO: &lt;span class="o"&gt;[&lt;/span&gt;init.js] Patching package.json at path: /Users/USERNAME/Git/testReactNativeDetox/package.json
detox[34202] INFO: &lt;span class="o"&gt;[&lt;/span&gt;init.js] json[&lt;span class="s2"&gt;"detox"&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"test-runner"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new folder called &lt;code&gt;e2e&lt;/code&gt; with a basic test and some initial configuration such as &lt;code&gt;init.js&lt;/code&gt;, which is the file that tells jest to start the simulator and so on. Let’s modify this initial test to check if the two first sections are visible.&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;Example&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;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reloadReactNative&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;should have "Step One" section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;should have "See Your Changes" section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;See Your Changes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;Next, you need to add a configuration for Detox inside your &lt;code&gt;package.json&lt;/code&gt;. Add the following object to the &lt;code&gt;detox&lt;/code&gt; key, replacing the name of &lt;code&gt;testReactNativeDetox&lt;/code&gt; with the name of your application:&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;"detox"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"test-runner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"ios.release"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"binaryPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./ios/build/Build/Products/Release-iphonesimulator/testReactNativeDetox.app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xcodebuild -workspace ios/testReactNativeDetox.xcworkspace -configuration release -scheme testReactNativeDetox -sdk iphonesimulator -derivedDataPath ios/build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ios.simulator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"iPhone X"&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;span class="p"&gt;}&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;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;Once done, try to build the application 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;~ detox build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case your build failed with the message &lt;code&gt;clang: error: linker command failed with exit code 1 (use -v to see invocation)&lt;/code&gt;, please refer to this &lt;a href="https://github.com/facebook/react-native/issues/4210#issuecomment-171944483" rel="noopener noreferrer"&gt;solution in GitHub issues&lt;/a&gt; and try running the command again.&lt;/p&gt;

&lt;p&gt;Finally, time to run the test!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ detox &lt;span class="nb"&gt;test

 &lt;/span&gt;PASS e2e/firstTest.spec.js &lt;span class="o"&gt;(&lt;/span&gt;7.514s&lt;span class="o"&gt;)&lt;/span&gt;
  Example
    ✓ should have &lt;span class="s2"&gt;"Step One"&lt;/span&gt; section &lt;span class="o"&gt;(&lt;/span&gt;260ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should have &lt;span class="s2"&gt;"See Your Changes"&lt;/span&gt; section &lt;span class="o"&gt;(&lt;/span&gt;278ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2As9LDDBS_DCZrkb1K" 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%2As9LDDBS_DCZrkb1K"&gt;&lt;/a&gt;&lt;em&gt;Initial Detox setup&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Time to make it fancier! 💅
&lt;/h3&gt;

&lt;p&gt;Let’s put those boring and flat sections inside a colorful carousel! Because who doesn’t love them?&lt;/p&gt;

&lt;p&gt;In order to save time, I decided to use an existing carousel component built by the community. For this demo, I used &lt;a href="https://github.com/oliviertassinari/react-swipeable-views" rel="noopener noreferrer"&gt;&lt;code&gt;react-swipeable-views-native&lt;/code&gt;&lt;/a&gt;. I’m sure there must be better alternatives out there, but this one was the perfect match for my needs.&lt;/p&gt;

&lt;p&gt;Also, in order to generate nice random colors, I used &lt;a href="https://github.com/davidmerfield/randomColor" rel="noopener noreferrer"&gt;&lt;code&gt;randomcolor&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install both libraries as dependencies for the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ yarn add react-swipeable-views-native randomcolor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I made a few modifications inside &lt;code&gt;App.js&lt;/code&gt; — you can find the code &lt;a href="https://github.com/EmaSuriano/e2e-react-native-detox/blob/master/App.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This is the summary of changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrap all the sections inside &lt;code&gt;SwipeableViews&lt;/code&gt; to enable swiping behavior&lt;/li&gt;
&lt;li&gt;Wrap each section inside a custom &lt;code&gt;View&lt;/code&gt; called &lt;code&gt;Slide&lt;/code&gt; that implements properties like &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;backgroundColor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Button&lt;/code&gt; and a &lt;code&gt;TextInput&lt;/code&gt; component to the last two slides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this is the result:&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%2F472%2F0%2Aj2QoheDX-W0hGE8L" 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%2F472%2F0%2Aj2QoheDX-W0hGE8L"&gt;&lt;/a&gt;&lt;em&gt;Demo with carousel&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Detox tests 🧪
&lt;/h3&gt;

&lt;p&gt;In order to make things easier, let’s add two new &lt;code&gt;scripts&lt;/code&gt; into the &lt;code&gt;package.json&lt;/code&gt;:&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;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"e2e: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;"detox test -c ios.release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"e2e:build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"detox build -c ios.release"&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;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;Now that the application has changed, you need to create a new build of it in order to run tests with the modified version. Execute 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;~ yarn e2e:build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process may take some time. In the meantime, let’s take a quick look at the existing tests:&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;Example&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;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reloadReactNative&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;should show "Step One"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;should show "See Your Changes"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;See Your Changes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// THIS TEST WILL FAIL!&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 second test will definitely fail because the “See Your Changes” section is now in the second slide of the Carousel, which is not visible for the user until they swipe. Therefore, let’s make Detox move to that slide!&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;Example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// previous tests here&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;should render "See Your Changes" in the second slide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// getting the reference of the slides and make a swipe&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;See Your Changes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// no this will pass!&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;At this point, you can execute the end-to-end tests, and they should pass! The command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ yarn e2e:test

 PASS e2e/firstTest.spec.js &lt;span class="o"&gt;(&lt;/span&gt;7.514s&lt;span class="o"&gt;)&lt;/span&gt;
  Example
    ✓ should have &lt;span class="s2"&gt;"Step One"&lt;/span&gt; section &lt;span class="o"&gt;(&lt;/span&gt;260ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should render &lt;span class="s2"&gt;"See Your Changes"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;the second slide &lt;span class="o"&gt;(&lt;/span&gt;993ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AD8wrl__Xw1Fg9uut" 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%2AD8wrl__Xw1Fg9uut"&gt;&lt;/a&gt;&lt;em&gt;Running a basic test&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s add a few more tests to cover the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test that the carousel allows the user to move back and forth inside the slides.&lt;/li&gt;
&lt;li&gt;Move the third slide and interact with the &lt;code&gt;Button&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Move the last slice and interact with the &lt;code&gt;TextInput&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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;Example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// previous tests here&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;should enable swiping back and forth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;right&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;should render "Debug" and have a Button to click in the third slide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Debug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click here!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;tap&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Clicked!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;should render "Learn More" and change text in the fourth slide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slides&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;swipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&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="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Learn More&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;docsInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docsInput&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;docsInput&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&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;docsInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearText&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;docsInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Maybe later!&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;docsInput&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Maybe later!&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feature fully tested! Let’s run the tests again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ yarn e2e:test

 PASS e2e/firstTest.spec.js &lt;span class="o"&gt;(&lt;/span&gt;22.128s&lt;span class="o"&gt;)&lt;/span&gt;
  Example
    ✓ should have &lt;span class="s2"&gt;"Step One"&lt;/span&gt; section &lt;span class="o"&gt;(&lt;/span&gt;268ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should render &lt;span class="s2"&gt;"See Your Changes"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;the second slide &lt;span class="o"&gt;(&lt;/span&gt;982ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="nb"&gt;enable &lt;/span&gt;swiping back and forth &lt;span class="o"&gt;(&lt;/span&gt;1861ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should render &lt;span class="s2"&gt;"Debug"&lt;/span&gt; and have a Button to click &lt;span class="k"&gt;in &lt;/span&gt;the third slide &lt;span class="o"&gt;(&lt;/span&gt;2710ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should render &lt;span class="s2"&gt;"Learn More"&lt;/span&gt; and change text &lt;span class="k"&gt;in &lt;/span&gt;the fourth slide &lt;span class="o"&gt;(&lt;/span&gt;9964ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AoXvblIgu_byjcI4w" 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%2AoXvblIgu_byjcI4w"&gt;&lt;/a&gt;Running all tests&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: Running E2E test in CI 🎁
&lt;/h3&gt;

&lt;p&gt;Running tests inside CI is quite important; they basically eliminate the need to do manual testing and prevent shipping bugs to production (in case we have the proper set of tests). For this example, I decided to use &lt;a href="https://travis-ci.org/" rel="noopener noreferrer"&gt;TravisCI&lt;/a&gt; because it has an amazing integration with GitHub and also provides an unlimited plan for open source projects.&lt;/p&gt;

&lt;p&gt;In case you are using GitHub, you can install the &lt;a href="https://github.com/marketplace/travis-ci" rel="noopener noreferrer"&gt;Travis Application&lt;/a&gt;, create a new plan, and allow it to access your repositories.&lt;/p&gt;

&lt;p&gt;After that, you need to create a new file inside your project called &lt;code&gt;.travis.yml&lt;/code&gt;, which defines the steps you want to run in CI.&lt;/p&gt;

&lt;p&gt;I tweaked the CI configuration inside the &lt;a href="https://github.com/wix/Detox/blob/master/docs/Guide.RunningOnCI.md#-running-detox-on-travis-ci" rel="noopener noreferrer"&gt;Official Documentation of Detox&lt;/a&gt; a little bit, and this is the one that works in my case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;language: objective-c
osx\_image: xcode10.2

branches:
  only:
    - master

env:
  global:
    - NODE\_VERSION=stable

install:
  - brew tap wix/brew
  - brew install applesimutils
  - curl -o- [https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh](https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh) | bash
  - export NVM\_DIR="$HOME/.nvm" &amp;amp;&amp;amp; [-s "$NVM\_DIR/nvm.sh"] &amp;amp;&amp;amp; . "$NVM\_DIR/nvm.sh"
  - nvm install $NODE\_VERSION
  - nvm use $NODE\_VERSION
  - nvm alias default $NODE\_VERSION

  - npm install -g react-native-cli
  - npm install -g detox-cli
  - npm install
  - cd ios; pod install; cd -;

script:
  - npm run e2e:ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last thing: add the command &lt;code&gt;e2e:ci&lt;/code&gt; into your &lt;code&gt;package.json&lt;/code&gt;. This command will build the application (&lt;code&gt;detox build&lt;/code&gt;), run the tests (&lt;code&gt;detox test&lt;/code&gt;), and close the emulator in order to finish the execution (&lt;code&gt;--cleanup&lt;/code&gt; flag).&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;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"e2e: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;"detox test -c ios.release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"e2e:build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"detox build -c ios.release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"e2e:ci"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run e2e:build &amp;amp;&amp;amp; npm run e2e:test -- --cleanup"&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;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;Once you pushed all the changes into your &lt;code&gt;master&lt;/code&gt; branch, try to open a new pull request. You should see a new pull request checker has been added, which will call Travis, and this one runs the Detox tests.&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%2F0%2ApToIacgZmMFgG8a6" 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%2ApToIacgZmMFgG8a6"&gt;&lt;/a&gt;&lt;em&gt;Travis PR Checker&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://travis-ci.org/EmaSuriano/e2e-react-native-detox/builds/585643904" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the link to the full log in Travis for that pull request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing words
&lt;/h3&gt;

&lt;p&gt;In case you were thinking of adding tests to your React Native application, I highly encourage you to go and try Detox! Detox is an amazing end-to-end testing solution for mobile, and after using it for quite some time, these are the pros and cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Very well abstracted syntax for matchers and to trigger specific actions&lt;/li&gt;
&lt;li&gt;✅ Integration with Jest is just wonderful&lt;/li&gt;
&lt;li&gt;✅ Possibility to run tests in CI&lt;/li&gt;
&lt;li&gt;❌ Sometimes you might encounter configuration errors, and finding the proper solution may take some time. The best way to address this problem is to go and take a deep look at the &lt;a href="https://github.com/wix/Detox/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more thing before you leave, I decided to start a newsletter so in case you want to hear about what I’m posting please consider following it! No SPAM, no hiring, no application marketing, just tech posts 👌&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gXvbF5" rel="noopener noreferrer"&gt;EmaSuriano Newsletter&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  References and further reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/EmaSuriano/e2e-react-native-detox" rel="noopener noreferrer"&gt;Demo Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wix/Detox/blob/master/docs/README.md" rel="noopener noreferrer"&gt;Detox Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/oliviertassinari/react-swipeable-views" rel="noopener noreferrer"&gt;react-swipeable-views-native&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/react-native-community/cli" rel="noopener noreferrer"&gt;react-native-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/davidmerfield/randomColor" rel="noopener noreferrer"&gt;randomColor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tutorialspoint.com/software_testing_dictionary/end_to_end_testing.htm" rel="noopener noreferrer"&gt;End-to-End Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wix.engineering/" rel="noopener noreferrer"&gt;Wix Engineering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>endtoendtesting</category>
      <category>reactnative</category>
      <category>frontenddev</category>
      <category>detox</category>
    </item>
    <item>
      <title>Docker for Frontend Developers</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Fri, 29 May 2020 11:04:44 +0000</pubDate>
      <link>https://dev.to/emasuriano/docker-for-frontend-developers-14bh</link>
      <guid>https://dev.to/emasuriano/docker-for-frontend-developers-14bh</guid>
      <description>&lt;p&gt;Article originally published on the &lt;a href="https://blog.logrocket.com/docker-for-front-end-developers/" rel="noopener noreferrer"&gt;LogRocket blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since Dockers’s release in 2013, the use of containers has been on the rise, and it’s now become a part of the stack in most tech companies out there. Sadly, when it comes to front-end development, this concept is rarely touched.&lt;/p&gt;

&lt;p&gt;Therefore, when front-end developers have to interact with containerization, they often struggle a lot. That is exactly what happened to me a few weeks ago when I had to interact with some services in my company that I normally don’t deal with.&lt;/p&gt;

&lt;p&gt;The task itself was quite easy, but due to a lack of knowledge of how containerization works, it took almost two full days to complete it. After this experience, I now feel more secure when dealing with containers and CI pipelines, but the whole process was quite painful and long.&lt;/p&gt;

&lt;p&gt;The goal of this post is to teach you the core concepts of Docker and how to manipulate containers so you can focus on the tasks you love!&lt;/p&gt;

&lt;h2&gt;
  
  
  The what and why for Docker 🤔
&lt;/h2&gt;

&lt;p&gt;Let’s take a look at a simple definition of what is Docker:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker is a tool that allows developers, sys-admins, etc. to easily deploy their applications in a sandbox (called containers) to run on the host operating system. — &lt;a href="https://docker-curriculum.com/" rel="noopener noreferrer"&gt;Docker Curriculum&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key benefit of using containers is that they package up code and all its dependencies so the application runs quickly and reliably regardless of the computing environment.&lt;/p&gt;

&lt;p&gt;This decoupling allows container-based applications to be deployed easily and consistently regardless of where the application will be deployed: a cloud server, an internal company server, or your personal computer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Wl-7swP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/730/0%2AHmu0pZbnOc0bmVlx" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Wl-7swP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/730/0%2AHmu0pZbnOc0bmVlx" width="730" height="632"&gt;&lt;/a&gt;Docker Architecture&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminology 📖
&lt;/h2&gt;

&lt;p&gt;In the Docker ecosystem, there are a few key definitions you’ll need to know to understand what the heck they are talking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Image&lt;/em&gt;: The blueprints of your application, which forms the basis of containers. It is a lightweight, standalone, executable package of software that includes everything needed to run an application, i.e., code, runtime, system tools, system libraries, and settings.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Containers&lt;/em&gt;: These are defined by the image and any additional configuration options provided on starting the container, including but not limited to the network connections and storage options.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Docker Daemon&lt;/em&gt;: The background service running on the host that manages the building, running, and distribution of Docker containers. The daemon is the process that runs in the OS the clients talk to.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Docker Client&lt;/em&gt;: The CLI that allows users to interact with the Docker daemon. It can also be in other forms of clients, too, such as those providing a UI interface.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Docker Hub&lt;/em&gt;: A registry of images. You can think of the registry as a directory of all available Docker images. If required, you can host your own Docker registries and pull images from there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  “Hello, World!” Demo 🌎
&lt;/h2&gt;

&lt;p&gt;To fully understand the aforementioned terminologies, let’s set up Docker and run an example.&lt;/p&gt;

&lt;p&gt;The first step is installing Docker on your machine. To do that, go to the official &lt;a href="https://docs.docker.com/ee/desktop/" rel="noopener noreferrer"&gt;Docker page&lt;/a&gt;, choose your current OS, and start the download. You might have to create an account.&lt;/p&gt;

&lt;p&gt;After installing Docker, open your terminal and execute &lt;code&gt;docker run hello-world&lt;/code&gt;. You should see the following message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ ~ docker run hello-world
Unable to find image &lt;span class="s1"&gt;'hello-world:latest'&lt;/span&gt; locally
latest: Pulling from library/hello-world
1b930d010525: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;Digest: sha256:6540fc08ee6e6b7b63468dc3317e3303aae178cb8a45ed3123180328bcc1d20f
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see what actually happened behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;docker&lt;/code&gt; is the command that enables you to communicate with the Docker client.&lt;/li&gt;
&lt;li&gt;When you run &lt;code&gt;docker run [name-of-image]&lt;/code&gt;, the Docker daemon will first check if you have a local copy of that image on your computer. Otherwise, it will pull the image from Docker Hub. In this case, the name of the image is &lt;code&gt;hello-world&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Once you have a local copy of the image, the Docker daemon will create a container from it, which will produce the message "Hello from Docker!"&lt;/li&gt;
&lt;li&gt;The Docker daemon then streams the output to the Docker client and sends it to your terminal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Node.js Demo 📦
&lt;/h2&gt;

&lt;p&gt;The “Hello, World!” demo was quick and easy, but the truth is we were not using all Docker’s capabilities. Let’s do something more interesting. Let’s run a Docker container using Node.js.&lt;/p&gt;

&lt;p&gt;So, as you might guess, we need to somehow set up a Node environment in Docker. Luckily, the Docker team has created an amazing marketplace where you can search for Docker images inside their public Docker Hub. To look for a Node.js image, you just need to type “node” in the search bar, and you most probably will &lt;a href="https://hub.docker.com/_/node" rel="noopener noreferrer"&gt;find this one&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;So the first step is to pull the image from the Docker Hub, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ ~ docker pull node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need to set up a basic Node app. Create a file called &lt;code&gt;node-test.js&lt;/code&gt;, and let’s do a simple HTTP request using &lt;a href="https://jsonplaceholder.typicode.com/" rel="noopener noreferrer"&gt;JSON Placeholder&lt;/a&gt;. The following snippet will fetch a Todo and print the title:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;https&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&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;todo&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`The title is "&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&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="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: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted to avoid using external dependencies like &lt;code&gt;node-fetch&lt;/code&gt; or &lt;code&gt;axios&lt;/code&gt; to keep the focus of the example just on Node and not in the dependencies manager.&lt;/p&gt;

&lt;p&gt;Let’s see how to run a single file using the Node image and explain the &lt;code&gt;docker run&lt;/code&gt; flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ docker run -it --rm --name my-running-script -v "$PWD":/usr/src/app -w /usr/src/app node node node-test.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-it&lt;/code&gt; runs the container in the &lt;code&gt;interactive&lt;/code&gt; mode, where you can execute several commands inside the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--rm&lt;/code&gt; automatically removes the container after finishing its execution.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name [name]&lt;/code&gt; provides a name to the process running in the Docker daemon.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v [local-path: docker-path]&lt;/code&gt; mounts a local directory into Docker, which allows exchanging information or access to the file system of the current system. &lt;em&gt;This is one of my favorite features of Docker!&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-w [docker-path]&lt;/code&gt; sets the working directory (start route). By default, this is /.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node&lt;/code&gt; is the name of the image to run. It always comes after all the &lt;code&gt;docker run&lt;/code&gt; flags.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node node-test.js&lt;/code&gt; are instructions for the container. These always come after the name of the image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output of running the previous command should be: &lt;code&gt;The title is "delectus aut autem"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  React.js demo ⚛️
&lt;/h2&gt;

&lt;p&gt;Since this post is focused on front-end developers, let’s run a React application in Docker!&lt;/p&gt;

&lt;p&gt;Let’s start with a base project. For that, I recommend using the &lt;code&gt;create-react-app&lt;/code&gt; CLI, but you can use whatever project you have at hand; the process will be the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ ~ npx create-react-app react-test
➜ ~ &lt;span class="nb"&gt;cd &lt;/span&gt;react-test
➜ ~ yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to see the homepage of the &lt;code&gt;create-react-app&lt;/code&gt; project. Then, let’s introduce a new concept, the &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In essence, a &lt;code&gt;Dockerfile&lt;/code&gt; is a simple text file with instructions on how to build your Docker images. In this file, you’d normally specify the image you want to use, which files will be inside and whether you need to execute some commands before building.&lt;/p&gt;

&lt;p&gt;Let’s now create a file inside the root of the &lt;code&gt;react-test&lt;/code&gt; project. Name this &lt;code&gt;Dockerfile&lt;/code&gt;, and write the following:&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="c"&gt;# Select the image to use&lt;/span&gt;
FROM node

&lt;span class="c"&gt;## Install dependencies in the root of the Container&lt;/span&gt;
COPY package.json yarn.lock ./
ENV NODE&lt;span class="se"&gt;\_&lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/node&lt;span class="se"&gt;\_&lt;/span&gt;modules
ENV &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/node&lt;span class="se"&gt;\_&lt;/span&gt;modules/.bin
RUN yarn

&lt;span class="c"&gt;# Add project files to /app route in Container&lt;/span&gt;
ADD &lt;span class="nb"&gt;.&lt;/span&gt; /app

&lt;span class="c"&gt;# Set working dir to /app&lt;/span&gt;
WORKDIR /app

&lt;span class="c"&gt;# expose port 3000&lt;/span&gt;
EXPOSE 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When working in &lt;code&gt;yarn&lt;/code&gt; projects, the recommendation is to remove the &lt;code&gt;node_modules&lt;/code&gt; from the &lt;code&gt;/app&lt;/code&gt; and move it to root. This is to take advantage of the cache that &lt;code&gt;yarn&lt;/code&gt; provides. Therefore, you can freely do &lt;code&gt;rm -rf node_modules&lt;/code&gt; inside your React application.&lt;/p&gt;

&lt;p&gt;After that, you can build a new image given the above &lt;code&gt;Dockerfile&lt;/code&gt;, which will run the commands defined step by step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ ~ docker image build &lt;span class="nt"&gt;-t&lt;/span&gt; react:test &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check if the Docker image is available, you can run &lt;code&gt;docker image ls&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;➜ ~ docker image &lt;span class="nb"&gt;ls
&lt;/span&gt;REPOSITORY TAG IMAGE ID CREATED SIZE
react &lt;span class="nb"&gt;test &lt;/span&gt;b530cde7aba1 50 minutes ago 1.18GB
hello-world latest fce289e99eb9 7 months ago 1.84kB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it’s time to run the container by using the command you used in the previous examples: &lt;code&gt;docker run&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ docker run -it -p 3000:3000 react:test /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be aware of the &lt;code&gt;-it&lt;/code&gt; flag, which, after you run the command, will give you a prompt inside the container. Here, you can run the same commands as in your local environment, e.g., &lt;code&gt;yarn start&lt;/code&gt; or &lt;code&gt;yarn build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To quit the container, just type &lt;code&gt;exit&lt;/code&gt;, but remember that the changes you make in the container won’t remain when you restart it. In case you want to keep the changes to the container in your file system, you can use the &lt;code&gt;-v&lt;/code&gt; flag and mount the current directory into &lt;code&gt;/app&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;➜ ~ docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app react:test /bin/bash

root@55825a2fb9f1:/app# yarn build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the command is finished, you can check that you now have a &lt;code&gt;/build&lt;/code&gt; folder inside your local project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion 👋
&lt;/h3&gt;

&lt;p&gt;This has been an amazing journey into the fundamentals of how Docker works. For more advanced concepts, or to cement your understanding of the discussed concepts in this article, I advise you to check out the references linked below.&lt;/p&gt;

&lt;p&gt;One more thing before you leave, I decided to start a newsletter so in case you want to hear about what I’m posting please consider following it! No SPAM, no hiring, no application marketing, just tech posts 👌&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gXvbF5" rel="noopener noreferrer"&gt;EmaSuriano Newsletter&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  References 🤓
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docker-curriculum.com/" rel="noopener noreferrer"&gt;Docker Curriculum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/resources" rel="noopener noreferrer"&gt;Docker Content Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aquasec.com/wiki/display/containers/Docker+Architecture" rel="noopener noreferrer"&gt;Docker Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nodejs/docker-node" rel="noopener noreferrer"&gt;Github repository for docker-node&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>teaching</category>
      <category>docker</category>
      <category>react</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building a collaborative Calendar with Google and Gatsby</title>
      <dc:creator>Ema Suriano</dc:creator>
      <pubDate>Wed, 27 May 2020 12:44:15 +0000</pubDate>
      <link>https://dev.to/emasuriano/building-a-collaborative-calendar-with-google-and-gatsby-bkd</link>
      <guid>https://dev.to/emasuriano/building-a-collaborative-calendar-with-google-and-gatsby-bkd</guid>
      <description>&lt;p&gt;About one week ago a friend of mine came to me for help, he wanted to create an online calendar for cultural events around the city. The idea was to create an application with a calendar showing all the upcoming events with the possibility that any person can add or edit new events.&lt;/p&gt;

&lt;p&gt;So in summary, the application needs to have the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Display a calendar with events and information about them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read the events from somewhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provide a way to add new events.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Breaking down the Challenge 👷‍♂️
&lt;/h2&gt;

&lt;p&gt;If we translate these requirements to technical points, I listed the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage&lt;/strong&gt;: Have a database to store the events&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt; Provide an &lt;em&gt;API&lt;/em&gt; to read/write from/to the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; A web app with two pages, one to add the events and another with a calendar to display the events.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seems like a lot of work right? But what if I only focus on the frontend and delegate the backend and storage? Now it doesn’t seem that long!&lt;/p&gt;

&lt;p&gt;Most probably you heard about &lt;a href="https://www.google.com/intl/en/forms/about/" rel="noopener noreferrer"&gt;Google Forms&lt;/a&gt; and &lt;a href="https://www.google.com/intl/en/sheets/about/" rel="noopener noreferrer"&gt;Google Sheets&lt;/a&gt;? Let me tell you that they work amazing as &lt;strong&gt;Backend&lt;/strong&gt; and &lt;strong&gt;Storage&lt;/strong&gt; 😄 This is how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Google Forms provides the page to add and edit events to the calendar. The only thing you need to do is ask the proper questions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google Forms has a hidden feature to save your results inside a Google Spreadsheet, so each answer will be mapped into a row inside of a table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google has made an excellent job by providing APIs for all their products (you just have to enable it), therefore now we can read the rows from the spreadsheets from our application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The missing part is the Frontend, I used &lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; to create the website with the calendar because of its great &lt;a href="https://www.gatsbyjs.org/tutorial/part-five/" rel="noopener noreferrer"&gt;Source Plugin&lt;/a&gt; system, allowing me to access the events information very easily.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I made a simple diagram to show the interaction between the different parts. Hope it’s clear now 😅&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Setting the Form 📝
&lt;/h2&gt;

&lt;p&gt;Pretty straight forward, create &lt;a href="http://forms.new/" rel="noopener noreferrer"&gt;a new Google Form&lt;/a&gt; asking all the questions regarding your events. Most probably you want to ask for the name, when, where, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/forms/d/e/1FAIpQLSeUNNgqdhcPlTaVqLb_NceeBd_XVTEBxGwr66wSUbFRArPo3A/viewform" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the link to the form I created in case you want to check how I structure the questions.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Saving answers inside a Spreadsheet 💾
&lt;/h2&gt;

&lt;p&gt;Inside the &lt;em&gt;Edit Mode&lt;/em&gt; of the Form, select the &lt;strong&gt;Responses&lt;/strong&gt; and click on the green Spreadsheet icon that says “&lt;em&gt;View responses in Sheets&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dudddc8hr5eohwutpyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dudddc8hr5eohwutpyk.png" alt="Location of “View responses in Sheets” button" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will automatically create a Spreadsheet for you linking each answer of the form to a column. And of course, every time someone adds a new event the spreadsheet will be updated too ✨&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Enabling Google API to read from the spreadsheet
&lt;/h2&gt;

&lt;p&gt;This step could be tricky the first time you do it, but like the others is super simple too. Navigate to your &lt;a href="https://console.developers.google.com/" rel="noopener noreferrer"&gt;Google API Dashboard&lt;/a&gt; and open the &lt;strong&gt;Credentials&lt;/strong&gt; located in the left panel. There click on “&lt;em&gt;Create credentials&lt;/em&gt;” and select “&lt;em&gt;Service account key&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w2j2x17plr3ejps973f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w2j2x17plr3ejps973f.png" alt="Creating a Service account key" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll be redirected to a new page to create the service account key, make sure the option of “&lt;em&gt;App Engine default service account&lt;/em&gt;” is selected and the &lt;em&gt;key type&lt;/em&gt; marked as &lt;strong&gt;JSON&lt;/strong&gt;. Click on Create and you will receive a JSON file with a lot of fields, for the moment store it somewhere on your computer because we are going to use more than once.&lt;/p&gt;

&lt;p&gt;Inside the JSON object, look for the key &lt;code&gt;client_email&lt;/code&gt; and copy the value of it which should be a valid email address. Open the Spreadsheet generated by Google Forms, click on the &lt;strong&gt;Share&lt;/strong&gt; button located at the top right corner, and add the mail. This is how you enable to read the document from an external application 🎉&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Displaying the events within a calendar 🗓
&lt;/h2&gt;

&lt;p&gt;Once we stored all the events inside the Spreadsheet and have a way to retrieve them, it’s time to add a proper UI which means a beautiful Calendar!&lt;/p&gt;

&lt;p&gt;I created a Gatsby starter with a 1-minute setup that connects the application with your spreadsheet and renders all the events inside a calendar. You can find the repository &lt;a href="https://github.com/EmaSuriano/gatsby-starter-event-calendar" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to bootstrap the project, I recommend using the &lt;code&gt;new&lt;/code&gt; command from Gatbsy, which will create a new folder, copy all the code from the repository and install all the dependencies.&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;&amp;gt;&lt;/span&gt; gatsby new event-calendar https://github.com/EmaSuriano/gatsby-starter-event-calendar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember the &lt;em&gt;JSON&lt;/em&gt; file you downloaded before? Now it’s time to use it! Create a new called &lt;code&gt;.env&lt;/code&gt; in the root of the project with the following information from your config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRIVATE_KEY= // value of private_key
PRIVATE_KEY_ID= // value of private_key_id
PROJECT_ID= // value of project_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is going to read all these credentials and grant access to access the information inside your Spreadsheet. This file is excluded inside the &lt;code&gt;.gitignore&lt;/code&gt;, therefore it would never be published.&lt;/p&gt;

&lt;p&gt;After this open &lt;code&gt;gatsby-config.js&lt;/code&gt; and look for options of &lt;code&gt;gatsby-source-google-sheets&lt;/code&gt;, and change the values of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;spreadsheetId&lt;/code&gt;: you can find this value at the ending of the Spreadsheet URL. &lt;code&gt;https://docs.google.com/spreadsheets/d/[spreadsheetId]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;worksheetTitle&lt;/code&gt;: this is the name of the sheet from which you want to read the information.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, you need to map your columns of the spreadsheet to the data structure that the calendar is expecting. I highly suggest running your queries inside &lt;a href="http://localhost:8000/___graphql" rel="noopener noreferrer"&gt;http://localhost:8000/___graphql&lt;/a&gt;, which is the GraphQL playground where you can write your queries and get the result immediately.&lt;/p&gt;

&lt;p&gt;In my case, I have to write the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventsQuery&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;span class="n"&gt;allGoogleSheetEventsRow&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;span class="n"&gt;edges&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;span class="n"&gt;node&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;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whatisthename&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;eventLink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;linktotheevent&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;span class="p"&gt;}&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;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;Now you are ready to run the project, fetching all the events inside your Spreadsheet and display them inside beautiful Calendars!️ Execute the following command inside your terminal:&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;&amp;gt;&lt;/span&gt; yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will trigger &lt;code&gt;gatsby develop&lt;/code&gt; that will run all the Static Queries inside the project and host your application inside &lt;a href="http://localhost:8000/" rel="noopener noreferrer"&gt;http://localhost:8000/&lt;/a&gt;. If you open it inside your browser you should see this Home page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikzg1nm4qz03xrufrc91.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikzg1nm4qz03xrufrc91.png" alt="Event Calendar Starter — Home" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you scroll just a little bit or click on &lt;strong&gt;See all the events&lt;/strong&gt; button, you should be able to see all the Calendars filled with the information of your events 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgofth3aea51qyetk6d5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgofth3aea51qyetk6d5.png" alt="Event Calendar Starter — Calendar view" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you want to know more about &lt;a href="https://github.com/EmaSuriano/gatsby-starter-event-calendar" rel="noopener noreferrer"&gt;Event Calendar Starter&lt;/a&gt;, I recommend you to check the documentation that includes sections not present in this article, like App Configuration, Theming, Project Structure, Deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Last words 👋
&lt;/h2&gt;

&lt;p&gt;I hope you find this article interesting and it has encouraged you to build your own events page for your own community or friends.&lt;/p&gt;

&lt;p&gt;One more thing before you leave, I decided to start a newsletter so in case you want to hear about what I’m posting please consider following it! No SPAM, no hiring, no application marketing, just tech posts 👌&lt;br&gt;
&lt;a href="http://eepurl.com/gXvbF5" rel="noopener noreferrer"&gt;&lt;strong&gt;EmaSuriano Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://grommet.io" rel="noopener noreferrer"&gt;Grommet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/brandonmp/gatsby-source-google-sheets" rel="noopener noreferrer"&gt;gatsby-source-google-sheets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.google.com/googleapi/answer/6158862?hl=en" rel="noopener noreferrer"&gt;Setting up API Keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;GatsbyJS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>gatsby</category>
      <category>google</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
