<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[BEGO, IT Consulting, Nikola Begedin]]></title><description><![CDATA[Technical articles, enterpreneurship tips, etc.]]></description><link>https://bego.hrundefined</link><image><url>https://bego.hrundefined/logos/logo-512.png</url><title>BEGO, IT Consulting, Nikola Begedin</title><link>https://bego.hrundefined</link></image><generator>GatsbyJS Casper Starter</generator><lastBuildDate>Tue, 09 Oct 2018 17:22:49 GMT</lastBuildDate><atom:link href="https://bego.hrundefined/rss.xml" rel="self" type="application/rss+xml"/><author><![CDATA[Nikola Begedib]]></author><copyright><![CDATA[Bego, IT Development and Consulting, owner Nikola Begedin © 2018]]></copyright><item><title><![CDATA[Persisting state between page loads on the backend with Elixir/Phoenix]]></title><description><![CDATA[What's the idea? Say you have a signup process that's more onboarding than account creation. The user submits information in multiple steps…]]></description><link>https://bego.hrundefined/keeping-state-with-elixir-genserver</link><guid isPermaLink="false">https://bego.hrundefined/keeping-state-with-elixir-genserver</guid><category><![CDATA[programming]]></category><category><![CDATA[elixir]]></category><category><![CDATA[phoenix]]></category><category><![CDATA[ecto]]></category><category><![CDATA[genserver]]></category><category><![CDATA[state]]></category><dc:creator><![CDATA[Nikola Begedib]]></dc:creator><pubDate>Tue, 09 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;whats-the-idea&quot;&gt;&lt;a href=&quot;#whats-the-idea&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What&apos;s the idea?&lt;/h2&gt;
&lt;p&gt;Say you have a signup process that&apos;s more onboarding than account creation.&lt;/p&gt;
&lt;p&gt;The user submits information in multiple steps, where the next step depends on the contents of the previous step, etc.&lt;/p&gt;
&lt;p&gt;You also don&apos;t have an advanced frontend. It&apos;s just a basic server-based application, so each step means navigating to a different page&lt;/p&gt;
&lt;p&gt;What if the user gives up half way through? How do you deal with that?&lt;/p&gt;
&lt;p&gt;Suddenly, you are left with a partially created account.&lt;/p&gt;
&lt;p&gt;Maybe that partial data is useless to you and you want to clean it up? Maybe you want to do something about it? Send an email? Perform some task?&lt;/p&gt;
&lt;p&gt;Ideally, you don&apos;t save anything into the database yet, because why waste resources. However, you do keep it somewhere where it&apos;s cheap and easy to retrieve.&lt;/p&gt;
&lt;p&gt;Then, when the user is all done, you save the data. When you detect the user gave up, you do something about it without having to sanitise anything in the database.&lt;/p&gt;
&lt;h3 id=&quot;do-it-with-a-code-classlanguage-textgenservercode&quot;&gt;&lt;a href=&quot;#do-it-with-a-code-classlanguage-textgenservercode&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Do it with a &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt;!&lt;/h3&gt;
&lt;p&gt;Elixir has a &lt;code class=&quot;language-text&quot;&gt;GenServers&lt;/code&gt; and they&apos;re simple to use for any of those tasks you want to do. In fact, they even make it easy to create a multi-step process in the first place!&lt;/p&gt;
&lt;h2 id=&quot;how&quot;&gt;&lt;a href=&quot;#how&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How?&lt;/h2&gt;
&lt;p&gt;Let&apos;s generate a basic app called &lt;strong&gt;&quot;wizard&quot;&lt;/strong&gt;, where a user creates their account and workspace.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;mix phx.new wizard --no-ecto&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;We don&apos;t need a repository for the purposes of demonstrating the concept, thus, &lt;code class=&quot;language-text&quot;&gt;--no-ecto&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next up, we add some routes for our onboarding flow&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;get &amp;quot;/step-1&amp;quot;, WizardController, :step_1
post &amp;quot;/step-1&amp;quot;, WizardController, :next_step
get &amp;quot;/step-2&amp;quot;, WizardController, :step_2
post &amp;quot;/step-2&amp;quot;, WizardController, :submit_steps
get &amp;quot;/doublecheck&amp;quot;, WizardController, :doublecheck&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;And then we also add our controller.&lt;/p&gt;
&lt;p&gt;I could&apos;ve used a generator for all this, but I didn&apos;t, and you can copy paste, so it&apos;s fine!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;# lib/wizard_web/controllers/wizard_controller.ex

defmodule WizardWeb.WizardController do
  use WizardWeb, :controller

  alias Wizard.Onboarding

  def step_1(conn, _params) do
    conn |&amp;gt; render(&amp;quot;step_1.html&amp;quot;)
  end

  def submit_step_1(conn, params) do
    conn.remote_ip |&amp;gt; Onboarding.submit_account(params)
    conn |&amp;gt; redirect(to: conn |&amp;gt; wizard_path(:step_2))
  end

  def step_2(conn, _params) do
    conn |&amp;gt; render(&amp;quot;step_2.html&amp;quot;)
  end

  def submit_step_2(conn, params) do
    conn.remote_ip |&amp;gt; Onboarding.submit_workspace(params)
    conn |&amp;gt; redirect(to: conn |&amp;gt; wizard_path(:doublecheck))
  end

  def doublecheck(conn, _params) do
    {account_params, workspace_params} =
      conn.remote_ip |&amp;gt; Onboarding.get_data()

    conn
    |&amp;gt; render(
      &amp;quot;doublecheck.html&amp;quot;,
      account_params: account_params,
      workspace_params: workspace_params
    )
  end
end&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;The idea is, the &lt;code class=&quot;language-text&quot;&gt;step_1&lt;/code&gt; action renders a form. The user then submits the form, calling the &lt;code class=&quot;language-text&quot;&gt;submit_step_1&lt;/code&gt; action.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;submit_step_1&lt;/code&gt; sends the submitted parameters to our &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt;, which stores them.&lt;/p&gt;
&lt;p&gt;Similarly, &lt;code class=&quot;language-text&quot;&gt;step_2&lt;/code&gt; renders another form, which submits another set of parameters with &lt;code class=&quot;language-text&quot;&gt;submit_step_2&lt;/code&gt;, which also stores those parameters to the &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then, we end up on redirected to the &lt;code class=&quot;language-text&quot;&gt;doublecheck&lt;/code&gt; action, where we retrieve both sets of parameters and render our template with those as the assigns.&lt;/p&gt;
&lt;p&gt;For demo purposes, the template does nothing else other than rendering the parameters.&lt;/p&gt;
&lt;p&gt;All 3 calls to the &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt; send &lt;code class=&quot;language-text&quot;&gt;conn.remote_ip&lt;/code&gt; as the first argument. The server uses the IP as a key to stare the data on a per-user basis.&lt;/p&gt;
&lt;p&gt;Here&apos;s what the &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt; looks like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;# lib/wizard/onboarding.ex
defmodule Wizard.Onboarding do
  @name __MODULE__

  use GenServer

  # API

  def start_link() do
    GenServer.start_link(@name, [], name: @name)
  end

  def submit_account(ip, params) do
    GenServer.cast(@name, {:submit_account, ip, params})
  end

  def submit_workspace(ip, params) do
    GenServer.cast(@name, {:submit_workspace, ip, params})
  end

  def get_data(ip) do
    GenServer.call(@name, {:get_data, ip})
  end

  # Callbacks

  @impl true
  def init(_) do
    {:ok, %{}}
  end

  @impl true
  def handle_cast({:submit_account, ip, params}, %{} = state) do
    # keep old workspace params, update account params
    new_state =
      state
      |&amp;gt; Map.update(ip, {nil, nil}, fn {_, workspace_params} -&amp;gt;
        {params, workspace_params}
      end)

    {:noreply, new_state}
  end

  @impl true
  def handle_cast({:submit_workspace, ip, params}, %{} = state) do
    # keep old account params, update workspace params
    new_state =
      state
      |&amp;gt; Map.update(ip, {nil, nil}, fn {account_params, _} -&amp;gt;
        {account_params, params}
      end)

    {:noreply, new_state}
  end

  @impl true
  def handle_call({:get_data, ip}, _from, %{} = state) do
    {:reply, state[ip], state}
  end
end&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;Again, emphasis on simplicity for this demo.&lt;/p&gt;
&lt;p&gt;Out &lt;code class=&quot;language-text&quot;&gt;state&lt;/code&gt; is a map and we use the IP as the key for storing individual bits of data.&lt;/p&gt;
&lt;p&gt;The value for each IP is a tuple, the first element of which are the account params submitted in step 1 and the second element the workspace params submitted in step 2.&lt;/p&gt;
&lt;p&gt;Note that using the IP as the data key probably isn&apos;t ideal, but again, this is about the idea, not the real life execution.&lt;/p&gt;
&lt;p&gt;For the sake of getting this running as soon as possible, this is our view and respective templates:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;# lib/wizard_web/views/wizard_view
defmodule WizardWeb.WizardView do
  use WizardWeb, :view
end&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;Nothing special about that.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/wizard_web/templates/wizard/step_1.html.eex&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Step &lt;span class=&quot;token attr-name&quot;&gt;1:&lt;/span&gt; Your personal info&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h3&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form_for &lt;span class=&quot;token attribute variable&quot;&gt;@conn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@conn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; wizard_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:submit_step_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div class&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text_input&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;form-control&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div class&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text_input&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;form-control&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div class&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; submit&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Next&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;btn btn-primary pull-right&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# lib/wizard_web/templates/wizard/step_1.html.eex&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Step &lt;span class=&quot;token attr-name&quot;&gt;2:&lt;/span&gt; Your workspace info&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h3&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form_for &lt;span class=&quot;token attribute variable&quot;&gt;@conn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@conn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; wizard_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:submit_step_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div class&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text_input&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;form-control&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div class&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt; back_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@conn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; wizard_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:step_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; link&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Back&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;to:&lt;/span&gt; back_path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;btn btn-default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; submit&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Doublecheck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;btn btn-success pull-right&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# lib/wizard_web/templates/wizard/doublecheck.html.eex&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;This is the data you &lt;span class=&quot;token attr-name&quot;&gt;submitted:&lt;/span&gt;&amp;lt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h3&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h4&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Account&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h4&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;pre&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@account_params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; Kernel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inspect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;pretty:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;pre&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h4&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Workspace&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h4&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;pre&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@workspace_params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; Kernel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inspect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;pretty:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;pre&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt; back_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@conn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; wizard_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:step_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; link&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Back&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;to:&lt;/span&gt; back_path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;btn btn-default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;As I said, the steps are forms, while the final step is a basic page that renders the data we have.&lt;/p&gt;
&lt;h2 id=&quot;where-to-from-here&quot;&gt;&lt;a href=&quot;#where-to-from-here&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Where to from here?&lt;/h2&gt;
&lt;p&gt;In a real world scenario, each step would do something more. For example, individual submit steps could cast changesets or process the data in some way.&lt;/p&gt;
&lt;p&gt;The controller could cast data into changesets and render validation errors, storing valid changesets into the &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt;, instead of raw parameters.&lt;/p&gt;
&lt;p&gt;As a final step, we would also have some sort of call which actually persists the data, but as I said, this is about the idea.&lt;/p&gt;
&lt;p&gt;The possibilities should be clear, though. &lt;strong&gt;It&apos;s trivial to manage state during user interaction and between page navigations using a &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;going-even-further&quot;&gt;&lt;a href=&quot;#going-even-further&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Going even further...&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Z2DU0qLfPIY&quot;&gt;LiveViews are soon to become a thing.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the meantime, you could use the same &lt;code class=&quot;language-text&quot;&gt;GenServer&lt;/code&gt;, in conjunction with phoenix channels, to inform the user about any long running tasks in the onboarding process in real time.&lt;/p&gt;
&lt;p&gt;Who says you need to use this in a primarily server-based app? Imagine the possibilities!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[About this site]]></title><description><![CDATA[BEGO, It Consulting and Application Development Trade business registered in Croatia Original registration info]]></description><link>https://bego.hrundefined/about</link><guid isPermaLink="false">https://bego.hrundefined/about</guid><dc:creator><![CDATA[Nikola Begedib]]></dc:creator><pubDate>Sat, 23 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;bego-it-consulting-and-application-development&quot;&gt;&lt;a href=&quot;#bego-it-consulting-and-application-development&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BEGO, It Consulting and Application Development&lt;/h1&gt;
&lt;p&gt;Trade business registered in Croatia&lt;/p&gt;&lt;/p&gt;
&lt;h3 id=&quot;original-registration-infoh3&quot;&gt;&lt;a href=&quot;#original-registration-infoh3&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Original registration info&lt;/h3&gt;&lt;/h3&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;strong&gt;Full name&lt;/strong&gt;&lt;/dt&gt;
  &lt;dd&gt;Bego, obrt za IT savjetovanje i razvoj aplikacija&lt;/dd&gt;
  &lt;dt&gt;&lt;strong&gt;Address&lt;/strong&gt;&lt;/dt&gt;
  &lt;dd&gt;Zrinskih i Frankopana 8, 42000 Varaždin, Croatia&lt;/dd&gt;
  &lt;dt&gt;&lt;strong&gt;Owner&lt;/strong&gt;&lt;/dt&gt;
  &lt;dd&gt;Nikola Begedin&lt;/dd&gt;
&lt;/dl&gt;</content:encoded></item><item><title><![CDATA[Setting up an Elixir/Phoenix project for acceptance testing with Cypress]]></title><description><![CDATA[What is Cypress? Cypress is this awesome new tool that works great with APIs and is as easy to setup as anything if you're mocking out your…]]></description><link>https://bego.hrundefined/cypress-elixir-phoenix-ecto-sandbox</link><guid isPermaLink="false">https://bego.hrundefined/cypress-elixir-phoenix-ecto-sandbox</guid><category><![CDATA[programming]]></category><category><![CDATA[elixir]]></category><category><![CDATA[phoenix]]></category><category><![CDATA[ecto]]></category><category><![CDATA[cypress]]></category><category><![CDATA[testing]]></category><category><![CDATA[e2e]]></category><dc:creator><![CDATA[Nikola Begedib]]></dc:creator><pubDate>Thu, 21 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;what-is-cypress&quot;&gt;&lt;a href=&quot;#what-is-cypress&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is Cypress?&lt;/h1&gt;
&lt;p&gt;Cypress is this awesome new tool that works great with APIs and is as easy to setup as anything if you&apos;re mocking out your backend.&lt;/p&gt;
&lt;p&gt;I&apos;ve recently moved from Ember to React and I&apos;ve been missing the test setup for acceptance (or end to end) tests a lot.&lt;/p&gt;
&lt;p&gt;What Ember does with ember-cli is that it, right out of the box, give you a command which runs test in an actual browser environment (used to be PhantomJS by default, but is now headless Chrome), where you perform/simulate actual user interaction and make assertions on what happens both in the UI and in the background.&lt;/p&gt;
&lt;p&gt;Cypress is an app, with a UI which does something similar, except it works with any framework. Further more, it provides you with an infrastructure which allows you to do even more.&lt;/p&gt;
&lt;h1 id=&quot;but-what-about-data&quot;&gt;&lt;a href=&quot;#but-what-about-data&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;But what about data?&lt;/h1&gt;
&lt;p&gt;With Ember, we usually ended up using either a fake server with fake data, or a mock server layer with mock responses to requests. &lt;code class=&quot;language-text&quot;&gt;ember-cli-mirage&lt;/code&gt; usually did the trick for us.&lt;/p&gt;
&lt;p&gt;That means that, technically, only our frontend app was acceptance/e2e tested, but there was still no guarantee it works correctly with our backend.&lt;/p&gt;
&lt;p&gt;However, now that I started using Cypress and an Elixir/Phoenix backend, I realised I could&apos;ve and now can easily do more than that.&lt;/p&gt;
&lt;h1 id=&quot;ecto-sandbox-mode&quot;&gt;&lt;a href=&quot;#ecto-sandbox-mode&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ecto Sandbox mode&lt;/h1&gt;
&lt;p&gt;Ecto, the data layer/library Phoenix uses, has this thing called a Sandbox mode.&lt;/p&gt;
&lt;p&gt;To elaborate, each test in Elixir/ExUnit runs in a separate erlang process. The database layer runs a pool of connections those processes to use to perform actions with the database.&lt;/p&gt;
&lt;p&gt;To allow easier management of the data each test accepts, and to allow easier running of these processes in parallel, Ecto provides a thing called a Sandbox mode.&lt;/p&gt;
&lt;p&gt;Effectively, each process, or each test, get&apos;s it&apos;s own sandboxed database, which runs in a single transaction, isolated from other processes, and is canceled when the process exits.&lt;/p&gt;
&lt;p&gt;That means that, within the test, we perceive data as changing, but as soon as the test ends, any changes are rolled back.&lt;/p&gt;
&lt;p&gt;This isn&apos;t on by default, but the process to set it up is relatively simple&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;# in config/test.exs
config :my_app, MyApp.Repo, pool: Ecto.Adapters.SQL.Sandbox

# in test_helper.ex
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)

# before each test, or in an ExUnit.Case
setup do
  # Explicitly get a connection before each test
  :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
end&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;Really, it&apos;s explained quite well in the &lt;a href=&quot;https://hexdocs.pm/ecto/Ecto.Adapters.SQL.Sandbox.html#module-example&quot;&gt;Ecto documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem is, this only works with &lt;code class=&quot;language-text&quot;&gt;ExUnit&lt;/code&gt;, so it only works with backend tests written in Elixir. If your app is a server-rendered backend app, with little to no client code, that&apos;s fine. Most of the time, it&apos;s not.&lt;/p&gt;
&lt;p&gt;Another problem is that the database adapter we&apos;re using needs to support this. The two Ecto comes prepackaged with, &lt;code class=&quot;language-text&quot;&gt;Ecto.Adapters.MySQL&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Ecto.Adapters.Postgres&lt;/code&gt; both support it, with &lt;code class=&quot;language-text&quot;&gt;MySQL&lt;/code&gt; supporting sandbox mode, but not with concurrent processes. For third party adapters, we&apos;ll have to look into the documentation, possibly open tickets, or even contribute to an open source project to get it working.&lt;/p&gt;
&lt;h1 id=&quot;ecto-sandbox-mode-with-a-frontend-app&quot;&gt;&lt;a href=&quot;#ecto-sandbox-mode-with-a-frontend-app&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ecto Sandbox Mode with a Frontend app&lt;/h1&gt;
&lt;p&gt;A progressive web application, a dynamic client application, a javascript app, a single page application, call it whatever you like, but most web sites or web apps have quite a lot of client-written behavior, which needs to be tested to and, ideally, those tests need to make sure it works with the current backend, which could easily be a separate project, making any syncing even harder.&lt;/p&gt;
&lt;p&gt;Fortunately, &lt;code class=&quot;language-text&quot;&gt;phoenix_ecto&lt;/code&gt; has us covered with a plug called &lt;code class=&quot;language-text&quot;&gt;[Phoenix.Ecto.Sql.Sandbox](https://hexdocs.pm/phoenix_ecto/main.html#concurrent-browser-tests)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You should read that documentation. In fact, you should read the documentation of any Elixir library you use. The amount of info there is usually sufficient to de anything.&lt;/p&gt;
&lt;p&gt;But to elaborate, here&apos;s how it works.&lt;/p&gt;
&lt;p&gt;First, I create a separate mix environment for running the dev server in sandbox mode. This will have to run in a background process, or in a separate terminal tab, since cypress prefers not to be in charge of starting and shutting down the server.&lt;/p&gt;
&lt;p&gt;I like to call this one e2e, so we add &lt;code class=&quot;language-text&quot;&gt;config/e2e.exs&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;# config/e2e.exs

# it&amp;#39;s basically a dev environment, with a minor change added to it
import_config &amp;quot;dev.exs&amp;quot;

# this is the minor change
config :my_app, sql_sandbox: true&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;Then, I add a couple of lines to the &lt;code class=&quot;language-text&quot;&gt;MyAppWeb.Endpoint&lt;/code&gt; module:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;# lib/my_app_web/endpoint.ex:

use Phoenix.Endpoint, otp_app: :sift

if Application.get_env(:my_app, :sql_sandbox) do
  plug(Phoenix.Ecto.SQL.Sandbox, at: &amp;quot;/sandbox&amp;quot;, header: &amp;quot;x-session-id&amp;quot;, repo: MyApp.Repo)
end&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;What that does is, if I run my server with &lt;code class=&quot;language-text&quot;&gt;MIX_ENV=e2e mix phx.server&lt;/code&gt;, the router will support two additional actions&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;POST /sandbox&lt;/code&gt; checks out a sandbox connection&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DELETE /sandbox&lt;/code&gt; checks in a sandbox connection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also adds a plug layer to all other routes which checks for an &lt;code class=&quot;language-text&quot;&gt;x-session-id&lt;/code&gt; header and assigns the handler for that request the checked out connection.&lt;/p&gt;
&lt;p&gt;Get where I&apos;m going with this?&lt;/p&gt;
&lt;p&gt;Our frontend testing tool can now&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;make a &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; request to &lt;code class=&quot;language-text&quot;&gt;/sandbox&lt;/code&gt; to check out a sandbox connection and get back an id token at the start of the test&lt;/li&gt;
&lt;li&gt;sign all further requests in that test with an &lt;code class=&quot;language-text&quot;&gt;x-session-id&lt;/code&gt; header, the value of which is that token, to use the checked out sandbox connection&lt;/li&gt;
&lt;li&gt;make a &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; request to &lt;code class=&quot;language-text&quot;&gt;/sandbox&lt;/code&gt;, with an &lt;code class=&quot;language-text&quot;&gt;x-session-id&lt;/code&gt; header, to check in the same sandbox connection at the end of the test&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The end result is, our frontend test runs isolated from other frontend tests, while at the same time being able to access your actual backend, instead of using a fake one.&lt;/p&gt;
&lt;h1 id=&quot;setting-up-cypress-to-use-a-phoenix-backend-in-ecto-sandbox-mode&quot;&gt;&lt;a href=&quot;#setting-up-cypress-to-use-a-phoenix-backend-in-ecto-sandbox-mode&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up Cypress to use a Phoenix backend in Ecto sandbox mode&lt;/h1&gt;
&lt;p&gt;I&apos;m assuming you&apos;ve already followed the Getting Started section of the cypress docs. The base API url has been set and everything is ready. Ideally, you already have a test running and hitting our &lt;code class=&quot;language-text&quot;&gt;MIX_ENV=e2e mix phx.server&lt;/code&gt; backend running in another tab, causing that backend to error out due to duplicate date (because Cypress is not triggering sandbox mode).&lt;/p&gt;
&lt;p&gt;First thing to note is that even if you set it up now, the tests will still likely fail. You have to do a &lt;code class=&quot;language-text&quot;&gt;MIX_ENV=e2e mix ecto.reset&lt;/code&gt; to clean up the database first.&lt;/p&gt;
&lt;h2 id=&quot;cypress-with-a-phoenixabsinthe-backend-and-a-react-apollo-frontend&quot;&gt;&lt;a href=&quot;#cypress-with-a-phoenixabsinthe-backend-and-a-react-apollo-frontend&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cypress with a Phoenix/Absinthe backend and a React Apollo frontend&lt;/h2&gt;
&lt;p&gt;This is setup where I&apos;ve taught myself this procedure.&lt;/p&gt;
&lt;p&gt;For checking out and checking in connections, I&apos;ve created a pair of cypress commands&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Commands&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;checkoutSession&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/sandbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    cache&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;no-store&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    method&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sessionId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sessionId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Commands&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;dropSession&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/sandbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    method&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DELETE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;x-session-id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;For signing each request made by the app with an &lt;code class=&quot;language-text&quot;&gt;x-session-id&lt;/code&gt; header, things get a bit tricky. What we need to do is to modify &lt;code class=&quot;language-text&quot;&gt;window.fetch&lt;/code&gt; slightly, since that&apos;s what Apollo uses. To do that, we make use of Cypress&apos; ability to inject code into the browser session it runs the test in.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;buildTrackableFetchWithSessionId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetch &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetchUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; headers &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetchOptions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modifiedHeaders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;x-session-id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    headers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modifiedOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchOptions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; modifiedHeaders&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetchUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modifiedOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;window:before:load&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; win &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;win&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;fetch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildTrackableFetchWithSessionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;What we did is we wrapped our &lt;code class=&quot;language-text&quot;&gt;window.fetch&lt;/code&gt; into a function which adds the necessary header to each request.&lt;/p&gt;
&lt;p&gt;Now, this is a tad complex. Ideally, we would make use of &lt;code class=&quot;language-text&quot;&gt;cy.server&lt;/code&gt; somehow, but I was unable to figure out out yet. If I do, or if someone else does, I&apos;ll be sure to post an update.&lt;/p&gt;
&lt;p&gt;There&apos;s another caveat we need to solve here that makes it slightly more complex.&lt;/p&gt;
&lt;p&gt;Our cypress test has no idea when it can end, so it also needs to wait for all outgoing requests to resolve or fail before starting checking the session back in. Otherwise, we might encounter trouble.&lt;/p&gt;
&lt;p&gt;This works automatically for Cypress with XHR requests, but for requests made through the Fetch API, it fails, so we need to make our code a tad more complex.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;increaseFetches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;fetchCount&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;fetchCount&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;decreaseFetches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;fetchCount&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;fetchCount&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;buildTrackableFetchWithSessionId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetch &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetchUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; headers &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetchOptions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modifiedHeaders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;x-session-id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    headers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modifiedOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchOptions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; modifiedHeaders&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetchUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modifiedOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;decreaseFetches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;decreaseFetches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;window:before:load&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; win &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;win&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;fetch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildTrackableFetchWithSessionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;What we do is keep a counter of active Fetch API requests in &lt;code class=&quot;language-text&quot;&gt;Cypress.env&lt;/code&gt;. We have a special command we then use to wait for all of them to run their course before checking the session back in.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Commands&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;waitForFetches&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;fetchCount&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitForFetches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;The command waits in 100 ms increments to see if the fetch count dropped to zero. I found it completely reliable for my usage, but tweaks may be required.&lt;/p&gt;
&lt;p&gt;The easiest way to put this command to use is to rewrite our &lt;code class=&quot;language-text&quot;&gt;dropSession&lt;/code&gt; command.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Commands&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;dropSession&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitForFetches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/sandbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      method&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DELETE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;x-session-id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;As an alternative for request waiting, we could have the &lt;code class=&quot;language-text&quot;&gt;ApolloClient&lt;/code&gt; not use &lt;code class=&quot;language-text&quot;&gt;Fetch&lt;/code&gt; in a test environment, but then we&apos;re forced to scatter our custom test-code around, while this approach can keep it in one place.&lt;/p&gt;
&lt;p&gt;Ideally, at some point Cypress will know to wait for outgoing Fetch API requests to and we can just remove this complexity.&lt;/p&gt;
&lt;h1 id=&quot;cypress-with-a-phoenix-json-api-backend-and-a-react-rest-frontend&quot;&gt;&lt;a href=&quot;#cypress-with-a-phoenix-json-api-backend-and-a-react-rest-frontend&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cypress with a Phoenix JSON API backend and a React REST frontend&lt;/h1&gt;
&lt;p&gt;Really, the approach here is near identical to the GraphQL approach, except, if the network layer of the app uses XHR instead of Fetch, we don&apos;t need to maintain a fetch counter and tests will wait properly by default.&lt;/p&gt;
&lt;p&gt;In that case, instead of overriding fetch, we would override the &lt;code class=&quot;language-text&quot;&gt;XMLHttpRequest.prototype.open&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;I didn&apos;t get a chance to actually test this, but the general approach would be something like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;overrideOpen&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; original &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;openWithHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    original&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRequestHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;x-session-id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;window:before:load&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; win &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    win&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;XMLHttpRequest.prototype.open&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;overrideOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;XMLHttpRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;open&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;h1 id=&quot;tip-pre-creating-data-for-tests-to-use&quot;&gt;&lt;a href=&quot;#tip-pre-creating-data-for-tests-to-use&quot; aria-hidden=&quot;true&quot; class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tip: Pre-creating data for tests to use&lt;/h1&gt;
&lt;p&gt;We can use different approaches here. One is to use &lt;code class=&quot;language-text&quot;&gt;cy.task()&lt;/code&gt; to execute outside code and seed the database that way. Another is to simply use the data layer our app likely already has and make direct requests for data creation to the API, after we&apos;ve checked out the session, but before the actual test started running.&lt;/p&gt;
&lt;p&gt;For example, with GraphQL, I created a &lt;code class=&quot;language-text&quot;&gt;cy.mutate(mutation, variables)&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot;&gt;
      &lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Commands&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mutate&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; variables&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;x-session-id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cypress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sessionId&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; mutation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; variables &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
&lt;p&gt;My &lt;code class=&quot;language-text&quot;&gt;buildClient&lt;/code&gt; function is the same I use in my app to build an &lt;code class=&quot;language-text&quot;&gt;ApolloClient&lt;/code&gt;. It allows me to pass in custom headers for all requests to use, which is what I do here.&lt;/p&gt;</content:encoded></item></channel></rss>