<?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[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://tech.dorianlupu.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 18:31:50 GMT</lastBuildDate><atom:link href="https://tech.dorianlupu.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[New domain for my technical blog]]></title><description><![CDATA[Hello everyone,
I wanted to let you all know that I will be changing the domain name of this technical blog from https://blog.dorianlupu.com to https://tech.dorianlupu.com.
The reason for this change is that I have long wanted to start a new blog abo...]]></description><link>https://tech.dorianlupu.com/new-domain-for-technical-blog</link><guid isPermaLink="true">https://tech.dorianlupu.com/new-domain-for-technical-blog</guid><dc:creator><![CDATA[Dorian Lupu]]></dc:creator><pubDate>Mon, 02 Jan 2023 06:34:28 GMT</pubDate><content:encoded><![CDATA[<p>Hello everyone,</p>
<p>I wanted to let you all know that I will be changing the domain name of this technical blog from <a target="_blank" href="https://blog.dorianlupu.com"><strong>https://blog.dorianlupu.com</strong></a> to <a target="_blank" href="https://tech.dorianlupu.com"><strong>https://tech.dorianlupu.com</strong></a>.</p>
<p>The reason for this change is that I have long wanted to start a new blog about my entrepreneurial experiences, and I want to use the original domain name (<a target="_blank" href="https://blog.dorianlupu.com"><strong>https://blog.dorianlupu.com</strong></a>) for that purpose.</p>
<p>I hope you will continue to follow my technical blog at its new home at <a target="_blank" href="https://tech.dorianlupu.com"><strong>https://tech.dorianlupu.com</strong></a>. Thank you again for your support and I look forward to continuing to bring you valuable content.</p>
]]></content:encoded></item><item><title><![CDATA[Level up your Capybara acceptance tests using SitePrism]]></title><description><![CDATA[TL;DR

If you struggle with maintaining brittle capybara tests that mix high-level verifications with low-level CSS selectors, you should consider using SitePrism, a semantic DSL for describing your web application.
You'll never have to hardcode CSS ...]]></description><link>https://tech.dorianlupu.com/level-up-your-capybara-acceptance-tests-using-siteprism</link><guid isPermaLink="true">https://tech.dorianlupu.com/level-up-your-capybara-acceptance-tests-using-siteprism</guid><category><![CDATA[SitePrism]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[capybara]]></category><category><![CDATA[#rspec]]></category><dc:creator><![CDATA[Dorian Lupu]]></dc:creator><pubDate>Tue, 08 Nov 2022 14:16:23 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-tldr">TL;DR</h1>
<blockquote>
<p>If you struggle with maintaining brittle capybara tests that mix high-level verifications with low-level CSS selectors, you should consider using SitePrism, a semantic DSL for describing your web application.</p>
<p>You'll never have to hardcode CSS selectors in your acceptance tests ever again, making the acceptance tests easier to write, maintain and understand.</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/site-prism/site_prism">https://github.com/site-prism/site_prism</a></div>
<h1 id="heading-tell-me-more">Tell me more</h1>
<p>For the rest of the article, I assume you are familiar with <a target="_blank" href="https://rubyonrails.org/">Ruby on Rails</a>, <a target="_blank" href="https://rspec.info/">RSpec</a> and <a target="_blank" href="http://teamcapybara.github.io/capybara/">Capybara</a></p>
<p>In the following examples, we will go through the process of adding new simple features to a basic application, where clients can place orders to buy large quantities of sand.</p>
<p>We want to make it as simple as possible for the clients to add new orders. Forms should be interactive, with field-level validations, providing inline validation errors when invalid values are being filled in.</p>
<p>I won't go into all the details about how those features were implemented, do not hesitate to leave a comment if you have any questions about the implementation. </p>
<p>What we will be focusing on is how :</p>
<ul>
<li>to write the acceptance tests using Capybara</li>
<li>to refactor those tests using SitePrism, a semantic DSL for describing your web application. </li>
</ul>
<p>To keep it simple, let's say our application allows users to order large quantities of sand. We want to test the form allowing the users to place orders. The form is very simple and it has, at least for now, only one field named <code>quantity</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667840516488/EiKJzY07h.png" alt="Screenshot 2022-11-07 at 18.01.28.png" /></p>
<h2 id="heading-testing-the-form-field">Testing the form field</h2>
<p>Let's assume that the client</p>
<ul>
<li>accepts orders in full metric tons (50 metric tons is valid, 50.1 metric tons is invalid)</li>
<li>wants to discourage orders for less than 5 tons of sand by displaying a warning message</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667842208853/IsTOQP5oF.gif" alt="Nov-07-2022 18-28-17.gif" /></p>
<p>In addition, our client wants validation errors displayed on the fly, without the user having to submit the form.</p>
<p>Implementing the acceptance test for these requirements would look like this </p>
<pre><code class="lang-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'rails_helper'</span>

RSpec.describe Order, <span class="hljs-symbol">type:</span> <span class="hljs-symbol">:feature</span> <span class="hljs-keyword">do</span>
  context <span class="hljs-string">'create form'</span> <span class="hljs-keyword">do</span>
    it <span class="hljs-string">"validates the form"</span> <span class="hljs-keyword">do</span>
      <span class="hljs-comment"># navigate to form</span>
      visit <span class="hljs-string">'/orders'</span>
      click_link(<span class="hljs-string">'Add Order'</span>)

      <span class="hljs-comment"># accept only numbers</span>
      find(<span class="hljs-symbol">:css</span>, <span class="hljs-string">"#order_quantity"</span>).fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'X'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(
        page.find_by_id(<span class="hljs-string">"order_quantity__error_message"</span>)
      ).to have_text(<span class="hljs-string">"Quantity is not a number"</span>)
      expect(page).not_to have_css(<span class="hljs-string">"#order_quantity__warning_message"</span>)

      <span class="hljs-comment"># accept only integers</span>
      find(<span class="hljs-symbol">:css</span>, <span class="hljs-string">"#order_quantity"</span>).fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'50.1'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(
        page.find_by_id(<span class="hljs-string">"order_quantity__error_message"</span>)
      ).to have_text(<span class="hljs-string">"Quantity must be an integer"</span>)
      expect(page).not_to have_css(<span class="hljs-string">"#order_quantity__warning_message"</span>)

      <span class="hljs-comment"># warning about small quantities</span>
      find(<span class="hljs-symbol">:css</span>, <span class="hljs-string">"#order_quantity"</span>).fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'3'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(page).not_to have_css(<span class="hljs-string">"#order_quantity__error_message"</span>)
      expect(
        page.find_by_id(<span class="hljs-string">"order_quantity__warning_message"</span>)
      ).to have_text(<span class="hljs-string">"Please avoid orders of less than 5 metric tons"</span>)

      <span class="hljs-comment"># valid order</span>
      find(<span class="hljs-symbol">:css</span>, <span class="hljs-string">"#order_quantity"</span>).fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'99'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(page).not_to have_css(<span class="hljs-string">"#order_quantity__error_message"</span>)
      expect(page).not_to have_css(<span class="hljs-string">"#order_quantity__warning_message"</span>)

      <span class="hljs-comment"># submit order &amp; check the confirmation message</span>
      click_button(<span class="hljs-string">"Create Order"</span>)
      expect(page).to have_text(<span class="hljs-string">"Order was successfully created"</span>)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This does the job, but it isn't pleasant. Having low level CSS selectors mixed with high-level acceptance checks will be hard to maintain in the long run. </p>
<h2 id="heading-refactoring-the-tests-using-site-prism">Refactoring the tests using site prism</h2>
<p>This is where the <a target="_blank" href="https://github.com/site-prism/site_prism">site_prism</a> gem comes into action. By describing our orders page and orders create form using the DSL provided by the gem, we could rewrite the acceptance test in the following way :</p>
<pre><code class="lang-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'rails_helper'</span>

RSpec.describe Order, <span class="hljs-symbol">type:</span> <span class="hljs-symbol">:feature</span> <span class="hljs-keyword">do</span>
  context <span class="hljs-string">'create form'</span> <span class="hljs-keyword">do</span>
    it <span class="hljs-string">"validates the form"</span> <span class="hljs-keyword">do</span>
      <span class="hljs-comment"># navigate to form</span>
      app.orders_page.load
      app.orders_page.add_button.click
      expect(app.order_new_form).to be_loaded

      <span class="hljs-comment"># accept only numbers</span>
      form = app.order_new_form
      form.quantity.input.fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'X'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(form.quantity.error).to have_text(<span class="hljs-string">"Quantity is not a number"</span>)
      expect(form.quantity).not_to have_warning

      <span class="hljs-comment"># accept only integers</span>
      form.quantity.input.fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'50.1'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(form.quantity.error).to have_text(<span class="hljs-string">"Quantity must be an integer"</span>)
      expect(form.quantity).not_to have_warning

      <span class="hljs-comment"># warning about small quantities</span>
      form.quantity.input.fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'3'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(form.quantity).not_to have_error
      expect(form.quantity.warning).to have_text(<span class="hljs-string">"Please avoid orders of less than 5 metric tons"</span>)

      <span class="hljs-comment"># valid order</span>
      form.quantity.input.fill_in <span class="hljs-symbol">with:</span> <span class="hljs-string">'99'</span>, <span class="hljs-symbol">fill_options:</span> { <span class="hljs-symbol">clear:</span> <span class="hljs-symbol">:backspace</span> }
      expect(form.quantity).not_to have_error
      expect(form.quantity).not_to have_warning

      <span class="hljs-comment"># submit order &amp; check the confirmation message</span>
      form.submit.click
      expect(app.orders_page).to be_loaded
      expect(app.orders_page.flash_notice).to have_text(<span class="hljs-string">"Order was successfully created"</span>)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Below are the pieces of code that made this possible.</p>
<p>First of all,  we added the site_prism gem to the Gemfile.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># Gemfile</span>

group <span class="hljs-symbol">:test</span> <span class="hljs-keyword">do</span>
  gem <span class="hljs-string">'site_prism'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>I'm using rspec to test this app, so we need to add the following lines to <code>spec/spec_helper.rb</code> file </p>
<pre><code class="lang-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'site_prism'</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'site_prism/all_there'</span> <span class="hljs-comment"># Optional but needed to perform more complex matching</span>
</code></pre>
<p>Next, we need descrive the orders page and the order create form page using the DSL provided by site_prism</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># spec/objects_pages/orders/index_page.rb</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pages::Orders::IndexPage</span> &lt; SitePrism::Page</span>
  set_url(<span class="hljs-string">"/orders"</span>)

  element <span class="hljs-symbol">:flash_notice</span>, <span class="hljs-string">'.alert.alert-primary'</span>
  element <span class="hljs-symbol">:add_button</span>, <span class="hljs-string">'a'</span>, <span class="hljs-symbol">text:</span> <span class="hljs-string">'Add Order'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<pre><code class="lang-ruby"><span class="hljs-comment"># spec/objects_pages/orders/new_form.rb</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pages::Orders::NewForm</span> &lt; SitePrism::Page</span>
  set_url(<span class="hljs-string">"/orders/new"</span>)

  section <span class="hljs-symbol">:quantity</span> , <span class="hljs-string">"#order_quantity__wrapper"</span> <span class="hljs-keyword">do</span>
    element <span class="hljs-symbol">:input</span>, <span class="hljs-string">"#order_quantity"</span>
    element <span class="hljs-symbol">:warning</span>, <span class="hljs-string">"#order_quantity__warning_message"</span>
    element <span class="hljs-symbol">:error</span>, <span class="hljs-string">"#order_quantity__error_message"</span>
  <span class="hljs-keyword">end</span>

  element <span class="hljs-symbol">:submit</span>, <span class="hljs-string">'input[type="submit"]'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>One last step, is to facilitate the access the the pages from the feature tests.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># spec/objects_pages/app.rb</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span></span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">orders_page</span></span>
    Pages::Orders::IndexPage.new
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">order_new_form</span></span>
    Pages::Orders::NewForm.new
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>For that purpose can use a shared context for all feature tests: </p>
<pre><code class="lang-ruby"><span class="hljs-comment"># spec/spec_helper.rb</span>

RSpec.shared_context <span class="hljs-string">"site_prism"</span> <span class="hljs-keyword">do</span>
  let(<span class="hljs-symbol">:app</span>) { App.new }
<span class="hljs-keyword">end</span>

RSpec.configure <span class="hljs-keyword">do</span> <span class="hljs-params">|config|</span>
  config.include_context <span class="hljs-string">"site_prism"</span>, <span class="hljs-symbol">type:</span> <span class="hljs-symbol">:feature</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, we barely scratched the surface of what is possible to do with site_prism. </p>
<p><strong>The main takeaway is that by describing your application's pages using the DSL provided by site prism, tests are easier to write, maintain and understand. </strong></p>
<p>I plan to explore in a future article the more advanced usages that can be done with site_prism. </p>
<p><em>Subscribe and you will be notified when new articles are published on this blog</em>.</p>
]]></content:encoded></item></channel></rss>