sodapopcan

sodapopcan

Mirage - Browserless testing for Hologram pages and components

Now that I’ve used this in anger for a week, I’d like to introduce 0.1.0 of Mirage, a browerless testing framework for Hologram pages and components.

I still consider this library highly experimental, so please see “goals” below.

Motivation

There are a few things about LiveView that I love equally and one of those things is LiveViewTest. As a solo dev and avid TDD’r, I often start with a LiveView test and work my way down. Having something akin to a full browser test finish in milliseconds was one of the experiences that got me addicted to LiveView.

I’ve recently become extremely enamoured with Hologram and wanted a similar experience. With Hologram being isomorphic, it lends itself to getting even closer to that “almost a real browser test” than LiveView! Mirage aims to provide a similar experience and does so with an API similar to that of PhoenixTest.

Goals

This is my first heavily LLM-assisted library and would basically not exist (yet) without them. I’ve still put many, many hours into this, learning about Hologram internals, and deciding how this should work. This contains pieces that are everywhere from hand-written to totally vibed (though it’s mostly in-between those). Hologram is a very young framework, and it’s not (yet) written in a way that easily facilitates easily writing a library like Mirage. For that reason, Mirage not only contains code copied directly from Hologram, it has a high level re-implementation of Hologram’s client side Runtime in Elixir. While this is somewhat sustainable thanks to LLMs, it’s certainly not ideal.

Therefore, I would like to make it clear that, at least for now, Mirage is meant for the Hologram community to experiment with this type of testing library and figure out if it’s something we really want and what it should ultimately look/feel like. I know Bart would eventually like an e2e solution built into Hologram, but I ultimately want browerless more than anything, so I really wanted to start exploring it now.

Notes

I announced Mirage last week in the Hologram Discord. If anyone has taken it for a test drive yet, be aware that there is a significant breaking change in 0.1.0.

Examples

defmodule MyApp.HomePageTest do
  use ExUnit.Case, async: true
  use Mirage.Page

  test "sign up", %{server: server} do
    server
    |> visit(MyApp.HomePage, my_param: "some-param")
    |> click_link("Sign-up")
    |> fill_in("Name", with: "Bender Bending Rodríguez")
    |> fill_in("Password", with: "wanna-kill-all-humans?")
    |> click_button("Submit")
    |> assert_page(MyApp.WelcomePage)
    |> assert_has("p", "Welcome, Bender!")
  end
end

You can also test components in isolation:

defmodule MyApp.Components.PoplarTrackerTest do
  use ExUnit.Case, async: true
  use Mirage.Component

  test "it counts" do
    ~HOLO"""
    <MyApp.Components.PoplarTracker cid="counter" eaten={0}>
      <p>{@user.name} eats too many poplars.</p>
    </MyApp.Components.PoplarTracker>
    """
    |> mount({MyApp, user: current_user})
    |> click_button("Eat a poplar")
    |> assert_has("p", "Number of poplars eaten: 1")
  end
end

Get it!

https://github.com/sodapopcan/mirage

Most Liked

bartblast

bartblast

Creator of Hologram

Congrats on the release, @sodapopcan! It’s exciting to see libraries starting to pop up around Hologram!

On testing in general: I do envision Hologram eventually shipping proper browser-based e2e tooling, but there’s absolutely a place for fast component output and behaviour testing alongside it. Honestly, I’d expect most tests in a typical Hologram app to be written that way - they’re cheap, fast, and cover the bulk of what you actually want to assert. You’d still want some portion of tests exercising the full pipeline end-to-end though, especially anywhere JS interop is involved, since that’s where the isomorphic story can leak.

I’m really curious to see what testing patterns emerge through Mirage as people use it on real projects. That kind of in-the-wild feedback is hard to get any other way, so I’ll definitely be watching how it evolves - and credit to you for taking the leap and figuring out so much of this on your own, especially given how rough the internals must have been to work against.

On making life easier for library authors: it’s something I care about and want to address, but some things genuinely need to happen in a specific order. A lot of this - middleware, permissions/authorization, proper e2e testing - intersects with the local-first direction Hologram is heading in, and local-first is pretty new territory for web dev, so the design space is hard to pin down until some foundational pieces settle. I’d rather not publish new APIs prematurely either - anything that ships has to be maintained, and I’d like to open up surfaces that fit the long-term vision instead of patching in extension points that might not survive. So bear with me on that front.

Thanks again for putting the work in on this - looking forward to seeing where it goes :slight_smile:

sodapopcan

sodapopcan

Absolutely to all of this. My strategy in LiveView is to treat LiveViewTests more like unit tests, testing single pages, usually with at most one redirect. I like these to be super thorough so that I can refactor and run the whole “unit” test suite frequently and not be met with a whole bunch of errors when I finally run the e2es. I use e2es for user journey tests that flow through multiple pages and, as a consequence, generally tests all the JS.

Working with internals has really not been that bad at all! LLMs make it relatively easy to ensure I’m covering my bases (although I know a few things are still missing). Please don’t get the impression that I’m even mildly trying to apply some passive aggressive pressure! I think this type of library is a perfect use-case for heavy LLM-use where, so long as its behaviour is correct, code quality doesn’t matter so much (although I still go through a lot of it and get either the LLM of myself to clean stuff up when the mess gets out of control). Of course, that “so long as its behaviour is correct” is incredibly important for a testing library, but if enough are interested in Mirage and use it, it should be easy to get any kinks sorted out.

For me the ultimate goal for Mirage would be for a to be invalidated by an official solution baked in Hologram, but no rush there! It also depends on how Hologram does this on whether Mirage would still need to exist or not. For example, LiveViewTest provides more “low level” primitives while PhoenixTest provides the higher level feature-test-like API that Mirage uses, so maybe it would make sense for Hologram to provide something more akin to LiveViewTest? I have no idea. There are lots of questions there, but the whole point of Mirage is so that I, and anyone who wants it, can have browless testing NOW and you can put off thinking about it for a while :slight_smile:

Where Next?

Popular in Announcing Top

Flo0807
Hello everyone! I am excited to share our heart project Backpex with you. After building several Phoenix applications, we realized that...
New
bradley
I’ve been working with Claude Code extensively and absolutely love it. However, I’ve come across the challenge of managing configuration ...
New
restlessronin
The repo is at GitHub - cyberchitta/openai_ex: Community maintained Elixir library for OpenAI API · GitHub. Docs are at OpenaiEx User Gu...
152 10380 134
New
bryanjos
Hi, I just published version 0.23.0 of Elixirscript. https://github.com/bryanjos/elixirscript/blob/master/CHANGELOG.md Most of the chan...
New
bryanjos
Hi, I wanted share a small library we at Revelry Labs made for rendering react components from the server side. There are instructions fo...
New
brainlid
LangChain is short for Language Chain. An LLM, or Large Language Model, is the “Language” part. This library makes it easier for Elixir a...
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_orde...
New
blatyo
The best overview for how things are tied together is this presentation. Modules and functions are pretty well documented at this point, ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36432 110
New
scohen
Lexical Lexical is a next-generation language server for the Elixir programming language. Features Context aware code completion As-you...
New

Other popular topics Top

AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

We're in Beta

About us Mission Statement