phoenix_test icon indicating copy to clipboard operation
phoenix_test copied to clipboard

Add ability to match on entire html

Open srcrip opened this issue 1 year ago • 8 comments

To retain the ability to match on html instead of a selector, you can use a helper function like this:

  def assert_html(session, text) do
    assert render(session.view) =~ text

    session
  end

I think this should probably just be added in as it's own function in the Assertions module.

srcrip avatar Jun 14 '24 18:06 srcrip

Yeah, I'm not opposed to the library adding something like that. I think I would name it assert_text since we're asserting there's some text on the page. I'd be happy to review a PR If you're up for it.

germsvel avatar Jul 03 '24 10:07 germsvel

I think assert_html may be preferable because assert_text implies plain text, ie, the html tags stripped away

srcrip avatar Jul 08 '24 19:07 srcrip

That's an interesting thought. I think we read it from different angles (which is always interesting!)

This is where my head's at 👇

As a user of the library, I would expect assert_html would help me assert that there's some HTML on the page:

assert_html(session, "<div>Could not find user</div>")

But my guess is that people want to use it as an "assert this text is on the page":

assert_text(session, "Could not find user")

I think that's also evidenced by the implementation you suggested -- where you have text as the argument being passed in (not html).

And if instead we wanted people to assert on HTML structure, I'm not sure I would recommend that. I typically recommend the opposite because asserting on HTML structure tends to make the tests brittle. For example, what if I add a class or id to my HTML in the first example? I've broken my test for no reason.

If we want to assert on certain aspects of HTML, we should use assert_has since it lets you target things with CSS selectors.

Finally, I think assert_text keeps in line with things like Wallaby's assert_text. We're not trying to keep in line with that, of course, but I do think assert_text might be what people expect to exist because of previous tools they've used.

germsvel avatar Jul 09 '24 12:07 germsvel

Looping back on this, we can introduce an assert_text/2 if people still want it.

But I think if we do, we should make it so assertions don't fail on apostrophes (or other characters like that). That's one of the problems with html_string =~ "some text" assertions:

assert html =~ "user's email"
# this ^ fails because of the apostrophe

assert html =~ "user&#39;s email"
# this succeeds

There is no world in which I want to write the second assertion. So it'd be nice if we could make assert_text(session, "user's email") work.

germsvel avatar Apr 11 '25 12:04 germsvel

That should be pretty doable by having it use that Plug method under the hood that does html escaping

srcrip avatar Apr 15 '25 19:04 srcrip

@srcrip that sounds good. If you (or someone else is interested in contributing this), I'd be happy to review a PR!

germsvel avatar Apr 17 '25 12:04 germsvel

I felt the need for an assertion like this as I was trying to port the test code generated by gen.live to use PhoenixTest today. Here are some examples:

test "lists all tasks", %{conn: conn, task: task} do
  {:ok, _index_live, html} = live(conn, ~p"/tasks")

  assert html =~ "Listing Tasks"
  assert html =~ task.title
end

test "lists all tasks", %{conn: conn, task: task} do
  conn
  |> visit(~p"/tasks")
  |> assert_has("h1", text: "Listing Tasks")
  |> assert_has("#tasks", text: task.title)
  |> assert_has("#tasks>tr", count: 1)
end

# another place - the message toast text is tested easily here
html = render(index_live)
assert html =~ "Task created successfully"
assert html =~ "some title"

conn
... # skipping irrelevant things
|> assert_path(~p"/tasks")
|> assert_has("#flash-group", text: "Task created successfully")
|> assert_has("#tasks>tr", text: @create_attrs.title)

siraj-samsudeen avatar Sep 29 '25 09:09 siraj-samsudeen

Hi German, can you pls check the implementation in the branch to see whether I am on the right track before I create a PR - I am totally new to this, both to Elixir and Phoenix - I have studied the code to align to your conventions as much as possible, but being a newbie, I might miss many things - can you check?

Also, based on my research and chat with multiple AIs, I arrived at a number of options for implementations and I am not fully sure which ones aligns with your thinking/coding style - i have left comments in the code to get your feedback. For example here - https://github.com/siraj-samsudeen/phoenix_test/blob/63455226ac189012c060ecfd0c5f115265a76982/lib/phoenix_test/assertions.ex#L492

siraj-samsudeen avatar Sep 30 '25 06:09 siraj-samsudeen