Mirage (Mirage v0.0.5)

Copy Markdown View Source

Browserless test framework for the Hologram web framework.

Mirage initializes a page or component and expands its template into a fully-resolved DOM that tests can make assertions and trigger events against.

Summary

Functions

Trigger a blur event on an element.

Checks a checkbox by its associated label text.

Selects a radio button by its associated label.

Trigger a $click event on the element matching the given CSS selector.

Click on a button by its text.

Click on a link by its text.

Fill in an input or textarea by its label.

Trigger a focus event on an element.

Mount a component in isolation.

Opens the current page HTML in the default browser.

"Reloads" the current page by revisiting it with the current params.

Selects an option in a <select> box by its label.

Triggers a $select event on a text input or textarea by its label selecting the text given to option_text.

Unchecks a checkbox by its associated label.

Entry point to create a session.

Scopes all operations within the given block to descendants of the element matching selector.

Scopes to the <article> whose first heading (h1 - h6) matches header.

Scopes to the <fieldset> whose <legend> matches legend.

Scopes to the <section> whose first heading (h1 - h6) matches header.

Assertions

Asserts that the session's DOM contains exactly one element matching the given CSS selector (and optional filters).

Asserts that we are on a specific page. Useful after redirect.

Asserts that the session's DOM does not contain any element matching the given CSS selector (and optional filters).

Functions

blur(session, selector, text_or_opts \\ [])

Trigger a blur event on an element.

Accepts the same options as Mirage.click/3.

check(session, label, opts \\ [])

Checks a checkbox by its associated label text.

Triggers the input's $change as well as its form's $change event (if it has one).

Matches exactly by default; pass exact: false to match substrings. Raises if no matching radio button is found, or if more than one matches.

choose(session, label, opts \\ [])

@spec choose(Mirage.Session.t(), String.t(), keyword()) :: Mirage.Session.t()

Selects a radio button by its associated label.

Triggers the input's $change event as well as its form's $change event (if there is one).

Labels may wrap the input or reference it via a for/id pair.

Matches exactly by default; pass exact: false to match substrings. Raises if no matching radio button is found, or if more than one matches.

Example

Profile
|> visit()
|> choose("robot")
|> assert_has("p", "Your gender is 'robot'")

click(session, selector, text_or_opts \\ [])

Trigger a $click event on the element matching the given CSS selector.

Any actions or commands will be run. If the click triggers a page navigation, the new page will be loaded into the session. When clicking a submit button that belongs to a form, that form's $submit event will be triggered.

Raises if no matching element with a $click handler is found, or if more than one matches.

Examples

SignUpPage
|> visit()
|> fill_in("Name", with: "Bender")
|> fill_in("Password", with: "killallhumans")
|> click("button", "Submit")
|> assert_page(WelcomePage)

HomePage
|> visit()
|> click("button", "Log out")

Options

  • :text - Match on the element's inner text.
  • :exact - Set to false to match on a substring of an element's text. Default is true meaning you must provide an exact match.

click_button(session, text, opts \\ [])

@spec click_button(Mirage.Session.t(), String.t(), keyword(any())) ::
  Mirage.Session.t()

Click on a button by its text.

If it's a submit button belonging to a form, it will trigger that form's $submit event.

This is otherwise shorthand for Mirage.click/3 with "button" as its selector.

click_link(session, text, opts \\ [])

@spec click_link(Mirage.Session.t(), String.t(), keyword(any())) :: Mirage.Session.t()

Click on a link by its text.

This is simply a shorthand for Mirage.click/3 with "a" as its selector.

fill_in(session, label, opts)

@spec fill_in(Mirage.Session.t(), String.t(), keyword()) :: Mirage.Session.t()

Fill in an input or textarea by its label.

Finds an input by its associated label and triggers the input's $change and as well as its form's $change event (if it has one).

Labels may be associated with their input either by wrapping the input (<label>Name <input/></label>) or via a for attribute matching the input's id.

Matches exactly by default; pass exact: false to match substrings. Raises if no matching label is found, or if more than one matches.

focus(session, selector, text_or_opts \\ [])

Trigger a focus event on an element.

Accepts the same options as Mirage.click/3.

mount(template_fn, context \\ [])

@spec mount(function(), {module(), keyword()} | [{module(), keyword()}]) ::
  Mirage.Session.t()

Mount a component in isolation.

Pass a ~HOLO template containing a single component. Props, cid, and slot content are all declared in the markup itself:

~HOLO"""
<MyApp.Components.PoplarTracker cid="counter" eaten={0} />
"""
|> mount()
|> click("button", "Eat a poplar")
|> assert_has("p", "Number of poplars eaten: 1")

Context can be provided as a {Namespace, key: value} tuple. Props declared with from_context will be populated from matching context values.

~HOLO"""
<MyApp.Components.PoplarTracker cid="counter">
  <p>{@user.name} eats too many poplars</p>
</MyApp.Components.PoplarTracker>
"""
|> mount({MyApp, user: current_user, theme: "dark"})

For multiple namespaces, use a list of tuples:

~HOLO"""
<MyApp.Dashboard cid="dash" />
"""
|> mount([{MyApp, user: current_user}, {Themes, mode: "dark"}])

open_browser(session, opts_or_open_fun \\ [])

@spec open_browser(Mirage.Session.t(), keyword() | function()) :: Mirage.Session.t()

Opens the current page HTML in the default browser.

session
|> fill_in("Name", with: "Philip")
|> open_browser()
|> assert_has("Philip")

When using with a component (via Mirage.mount/2), the output will be wrapped in a thin layout bringing in your app's styles.

reload(session)

@spec reload(Mirage.Session.t()) :: Mirage.Session.t()

"Reloads" the current page by revisiting it with the current params.

All client-side state (component state, checked radios, etc.) is reset, just like a real browser reload.

session
|> fill_in("Name", with: "Leela")
|> reload()
|> refute_has("input", value: "Leela")

select(session, label, option_text, opts \\ [])

Selects an option in a <select> box by its label.

Triggers the select's $change event with the option's value attribute (defaulting to the option's inner text when no value attribute is present).

Labels may wrap the select element or reference it via a for/id pair.

Works with multi-selects.

Matches exactly by default; pass exact: false to match substrings. Raises if no matching label or option is found, or if more than one matches.

Example

EditProfilePage
|> visit()
|> select("Company", "Planet Express")
|> assert_has("p", "You work for 'Planet Express'")

select_text(session, label, text_or_opts \\ [])

@spec select_text(Mirage.Session.t(), String.t(), String.t() | keyword()) ::
  Mirage.Session.t()

Triggers a $select event on a text input or textarea by its label selecting the text given to option_text.

When text is omitted, all text in the input is selected.

Raises if the label does not point to an input that accepts text (i.e. raises for checkboxes, radios, selects, and non-text input types).

Examples

session
|> fill_in("Bio", with: "I'm a bending unit. I bend girders.")
|> select_text("Bio", "girder")

session
|> fill_in("Bio", with: "My hobbies include smoking cigars, drinking, and killing all humans")
|> select_text("Bio")

sigil_HOLO(term, modifiers)

(macro)

uncheck(session, label, opts \\ [])

@spec uncheck(Mirage.Session.t(), String.t(), keyword()) :: Mirage.Session.t()

Unchecks a checkbox by its associated label.

Trigger's the input's $change event as well as its form's $change event (if it has one).

Matches exactly by default; pass exact: false to match substrings. Raises if no matching radio button is found, or if more than one matches.

visit(page_module, params \\ [])

@spec visit(
  module(),
  keyword()
) :: Mirage.Session.t()

Entry point to create a session.

Takes a Hologram.Page and, optional, any params. It returns a session which the rest of Mirage can use.

within(session, selector, fun)

Scopes all operations within the given block to descendants of the element matching selector.

session
|> within(".sidebar", fn session ->
  session
  |> assert_has("a", "Home")
  |> click_link("Home")
end)

within_article(session, header, fun)

@spec within_article(Mirage.Session.t(), String.t(), (Mirage.Session.t() ->
                                                  Mirage.Session.t())) ::
  Mirage.Session.t()

Scopes to the <article> whose first heading (h1 - h6) matches header.

session
|> within_article("Blog Post", fn session ->
  assert_has(session, "p", "Post content")
end)

within_fieldset(session, legend, fun)

@spec within_fieldset(Mirage.Session.t(), String.t(), (Mirage.Session.t() ->
                                                   Mirage.Session.t())) ::
  Mirage.Session.t()

Scopes to the <fieldset> whose <legend> matches legend.

session
|> within_fieldset("Account", fn session ->
  assert_has(session, "input#username")
end)

within_section(session, selector \\ "section", header, fun)

@spec within_section(
  Mirage.Session.t(),
  String.t(),
  String.t(),
  (Mirage.Session.t() ->
     Mirage.Session.t())
) ::
  Mirage.Session.t()

Scopes to the <section> whose first heading (h1 - h6) matches header.

session
|> within_section("Settings", fn session ->
  assert_has(session, "Send me update", "No")
end)

This can also be used more generally when given a CSS selector as the second argument.

session
|> within_section("div[role=article]", "My header", fn session ->
  assert_has(session, "p", "content")
end)

Assertions

assert_has(session, selector, text_or_opts \\ [])

Asserts that the session's DOM contains exactly one element matching the given CSS selector (and optional filters).

Raises if no element matches or if more than one element matches.

session
|> assert_has("button")
|> assert_has("h1", "Welcome")
|> assert_has("input#email", value: "bender@planetexpress.com")

Options

  • :text — also require the element's inner text (trimmed) to equal this value
  • :value — also require the element's value attribute to equal this value
  • :at — match only the element at this 1-based position among all nodes matching the selector

assert_page(session, page, expected_params \\ [])

@spec assert_page(Mirage.Session.t(), module(), keyword()) ::
  Mirage.Session.t() | no_return()

Asserts that we are on a specific page. Useful after redirect.

Optionally takes a keyword list of expected params:

session
|> click_link("Profile")
|> assert_page(ProfilePage, user_id: 42)

refute_has(session, selector, text_or_opts \\ [])

Asserts that the session's DOM does not contain any element matching the given CSS selector (and optional filters).

session
|> refute_has(".error")
|> refute_has("p", text: "Deleted")

Accepts the same options as assert_has/3.