Skip to content

Comments

Allow text/value arguments to be non-binaries#294

Merged
germsvel merged 2 commits intogermsvel:mainfrom
randycoulman:call-safe-to-iodata
Feb 4, 2026
Merged

Allow text/value arguments to be non-binaries#294
germsvel merged 2 commits intogermsvel:mainfrom
randycoulman:call-safe-to-iodata

Conversation

@randycoulman
Copy link
Contributor

Phoenix uses the Phoenix.HTML.Safe protocol to allow non-binary values to be safely rendered in the DOM.

It would be very convenient if assert_has/refute_has would more closely match the code under test.

This PR modifies assert_has and refute_has to call Safe.to_iodata (followed by iodata_to_binary) on their text and value arguments.

This allows callers to do do things like:

assert_has(session, ".answer", 42)
assert_has(session, ".answer", text: 42)
assert_has(session, "Active Date", value: ~D[2026-01-10])
# etc.

I originally tried always passing text and value to Safe.to_iodata and iodata_to_binary, but that ended up failing a bunch of tests because the default implementation of Safe.to_iodata does HTML escaping on binaries, so I added a helper function that only calls the pipeline for non-binary values.

Phoenix uses the `Phoenix.HTML.Safe` protocol to allow non-binary values to be safely rendered in the DOM.

It would be very convenient if `assert_has`/`refute_has` would more closely match the code under test.

This PR modifies `assert_has` and `refute_has` to call `Safe.to_iodata` (followed by `iodata_to_binary`) on their `text` and `value` arguments.

This allows callers to do do things like:

```
assert_has(session, ".answer", 42)
assert_has(session, ".answer", text: 42)
assert_has(session, "Active Date", value: ~D[2026-01-10])
# etc.
```

I originally tried always passing text and value to `Safe.to_iodata` and `iodata_to_binary`, but that ended up failing a bunch of tests because the default implementation of `Safe.to_iodata` does HTML escaping on binaries, so I added a helper function that only calls the pipeline for non-binary values.
@germsvel
Copy link
Owner

Thanks for opening this @randycoulman!

My one hesitation with this is... will people pass a date struct but expect the date to be in a different format? I can imagine it's an easy mistake to make. That's why thus far, it's always required passing explicit text.

Also, is there somewhere in the docs we could point out that we "fallback" to the Phoenix.HTML.Safe representation of the values? And that it allows you to pass custom structs that implement the protocol?

@randycoulman
Copy link
Contributor Author

Thanks for looking at this @germsvel!

I'm happy to update some docs if you think this PR is worth merging. I wanted to get the idea in front of you first to see what you thought.

I don't feel like I understand your concern about date structs. Can you say more? I'll write some words that will hopefully help, but if not, please clarify.

With phoenix_test as it is, if I tried to do assert_has("#some_field", text: ~D[2026-01-24]), I'll get a FunctionClauseError. With this PR applied, the date will get converted to ISO format ("2026-01-24") and the comparison will be done against that value. If the code under test formats the date differently (say as US format "01/24/2026"), then the assertion fails because it can't find an element with the expected text. The test would have to be written to format the date the same way as the code under test.

Because Phoenix uses Phoenix.HTML.Safe under the hood, I can render a date directly and it will convert to ISO format automatically and with this change, I can write my assertion the same way - by passing the date directly to the assertion.

I note that I can already pass a date struct to fill_in, but that's because it calls to_string under the hood. But I can't do the same with assert_has without this PR.

Update the function docs for `assert_has/3` and `refute_has/3` to mention the use of the `Phoenix.HTML.Safe` protocol.
@randycoulman
Copy link
Contributor Author

@germsvel I've added a paragraph to the docs for assert_has/3 and refute_has/3. Let me know if that's sufficient, or if you'd like more detail there.

Copy link
Owner

@germsvel germsvel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @randycoulman ! Looks great!

@germsvel germsvel merged commit 2ed0788 into germsvel:main Feb 4, 2026
2 checks passed
@randycoulman randycoulman deleted the call-safe-to-iodata branch February 5, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants