-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
510 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Accessors Cheatsheet | ||
|
||
Accessors let you access certains parts of the element you have selected previously. | ||
As the given htmlable can contain multiple nodes, these functions return lists. | ||
|
||
All function that can be filtered using a selector, they are done by using a selector | ||
as second argument. In that case, there is a version of the same function ended with | ||
`_in` where the first and second arguments are switched. These are just convenience | ||
function in case you want to pipe when building a complex selector. | ||
|
||
## Selecting nodes | ||
{: .col-2} | ||
|
||
#### All matching nodes | ||
|
||
```elixir | ||
find(view, "p") | ||
find_in("p", view) | ||
# Returns all paragraphs. | ||
``` | ||
|
||
#### First matching node | ||
|
||
```elixir | ||
find_first(view, "div.contents") | ||
find_first_in("div.contents", view) | ||
# Returns the first div with contents class | ||
``` | ||
|
||
#### Count the matching nodes | ||
|
||
Just a convinience function. | ||
|
||
```elixir | ||
find_count(view, "p") | ||
find_count_in("p", view) | ||
# Counts the number of paragraphs | ||
|
||
find_count(view, "nav ul li") | ||
find_count_in("nav ul li", view) | ||
# Counts the number of elements in the navigation list | ||
``` | ||
|
||
## Selecting attributes | ||
{: .col-2} | ||
|
||
### Classes | ||
|
||
Classes are quite common in HTML. Specially useful when checking for classes like | ||
`hidden` in Tailwind, that checks if an element should be hidden or not. Class | ||
attributes will be split in words before being returned. | ||
|
||
#### One element | ||
|
||
Look out for the nested list! | ||
|
||
```elixir | ||
classes(~s[<div class="one two three">Content</div>]) | ||
# [["one", "two", "three"]] | ||
``` | ||
|
||
#### Several elements | ||
|
||
```elixir | ||
classes(~s[<div class="one two three">Content</div><div class="four five">Other</div>]) | ||
# [["one", "two", "three"], ["four", "five"]] | ||
``` | ||
|
||
#### Using selectors | ||
`classes/2` let's you pass a selector as second argument. This is convenient so you don't | ||
to pipe `find` just for this. | ||
|
||
```elixir | ||
classes(~s[<div class="one two three">Content</div><div class="first">Other</div>], ".one") | ||
classes_in(".one", ~s[<div class="one two three">Content</div><div class="first">Other</div>]) | ||
# [["one", "two", "three"]] | ||
classes(~s[<div class="one two three">Content</div><div class="four five">Other</div>], "div") | ||
classes_in("div", ~s[<div class="one two three">Content</div><div class="four five">Other</div>]) | ||
# [["one", "two", "three"], ["four", "five"]] | ||
``` | ||
|
||
### Other attributes | ||
|
||
Other attributes can easily be accessed too. Unlike classes, these values are returned | ||
as strings, unparsed in any way. | ||
|
||
#### One element | ||
|
||
```elixir | ||
attribute(~s[<li data-index="0">1</li>], "data-index") | ||
# ["0"] | ||
``` | ||
|
||
#### Several elements | ||
|
||
```elixir | ||
attribute(~s[<li data-index="0">1</li><li data-index="1">2</li>], "data-index") | ||
# ["0", "1"] | ||
``` | ||
|
||
#### Using selectors | ||
|
||
`attribute/3` let's you pass a selector as second argument. This is convenient so you don't | ||
to pipe `find` just for this. | ||
|
||
|
||
```elixir | ||
attribute(~s[<div class="one" data-index="0">Content</div><div class="first" data-index="1">Other</div>], ".one", "data-index") | ||
attribute_in(".one", ~s[<div class="one" data-index="0">Content</div><div class="first" data-index="1">Other</div>], "data-index") | ||
# ["0"] | ||
attribute_in("div", ~s[<div class="one two three">Content</div><div class="four five">Other</div>], "class") | ||
# ["one two three", "four five"] | ||
``` | ||
|
||
### Text | ||
|
||
#### Several elements, combined | ||
|
||
Text would return just one string for all elements by default. | ||
|
||
```elixir | ||
text(~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>), ".odd") | ||
text_in(".odd", ~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>)) | ||
# "First Third" | ||
|
||
text(~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>), ".even") | ||
text_in(".even", ~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>)) | ||
# "Second" | ||
|
||
text(~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>), ".none") | ||
text_in(".none", ~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>)) | ||
# "" | ||
|
||
text(~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>), "li") | ||
text_in("li", ~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>)) | ||
# "First Second Third" | ||
``` | ||
|
||
#### Several elements, list | ||
|
||
Combine `Enum.map/2` with `Nobs.Accessors.text/3` to create a list. | ||
|
||
```elixir | ||
find(~s(<ul><li class="odd"> First </li> <li class="even"> Second </li> <li class="odd"> Third </li></ul>), "li") |> Enum.map(&text/1) | ||
# ~w(First Second Third) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Assertions Cheatsheet | ||
|
||
These ways of asserting in tests give you some advantages over the "standard" way: | ||
|
||
- Better errors. Most of these recipes rely on ExDoc to provide better errors when a | ||
test fails (except for [`is_in?/2`](`DomHelpers.Assertions.is_in?/2`) which is just a | ||
convenience function). | ||
- Forces you to be more specific about what you are testing. The usual way of using | ||
`render(view) =~ expected_content` is prone to false green and, when failing, makes | ||
it harder to know the intent of the test. | ||
|
||
## Elements existence | ||
{: .col-2} | ||
|
||
#### [`is_in?/2`](`DomHelpers.Assertions.is_in?/2`) | ||
|
||
Just a convenience function that reverses the arguments of `Phoenix.LiveViewTest.has_element?/2`. | ||
|
||
```elixir | ||
assert "input" | ||
|> with_attrs(type: "checkbox", name: {:ends_with, "[type]"}) | ||
|> is_in?(view) | ||
``` | ||
|
||
#### Check the number of elements matching a selector | ||
|
||
Other comparisons are possible, like greater than or less than. | ||
|
||
```elixir | ||
assert find_count(view, "[data-test=accordion]") == 2 | ||
``` | ||
|
||
## Elements attributes | ||
{: .col-2} | ||
|
||
#### Checking it has a class | ||
|
||
```elixir | ||
assert selector | ||
|> with_class(expected_class) | ||
|> is_in?(view) | ||
assert expected_class in (find_first(view, selector) |> classes() |> List.first()) | ||
``` | ||
|
||
#### Checking it has certain attribute | ||
|
||
```elixir | ||
assert selector | ||
|> with_attr("aria-expanded", "true") | ||
|> is_in?(view) | ||
assert [["true"]] == | ||
attribute(view, selector, "aria-expanded") | ||
``` | ||
|
||
#### Checking the text of an element | ||
|
||
When fails, you get a nice diff between both text. Also, `=~` is prone to false greens. | ||
|
||
```elixir | ||
assert text(view, "[data-test=my-element]") == "My content" | ||
``` | ||
|
||
#### Multiple checks at once | ||
|
||
No better errors here, except for text. | ||
|
||
```elixir | ||
selector = "button" |> with_class(".destroy") |> with_attrs(disabled: true, type: "submit") | ||
|
||
assert text(view, selector) == "Submit form" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Selectors Cheatsheet | ||
|
||
## Building selectors | ||
{: .col-2} | ||
|
||
### Adding class / classes to a selector | ||
|
||
#### Just one class | ||
|
||
```elixir | ||
with_class("span", "hidden") | ||
``` | ||
|
||
#### Several classes | ||
|
||
```elixir | ||
with_classes("span", ~w(contents grid)) | ||
``` | ||
|
||
### Checking no class in a selector | ||
|
||
#### Just one class | ||
|
||
```elixir | ||
without_class("span", "hidden") | ||
``` | ||
|
||
#### Several classes | ||
|
||
```elixir | ||
without_classes("span", ~w(contents grid)) | ||
``` | ||
|
||
## Id | ||
{: .col-1} | ||
|
||
### With id | ||
|
||
```elixir | ||
with_id("input", "form_field") | ||
``` | ||
|
||
## Attributes | ||
{: .col-2} | ||
|
||
### Adding attributes to a selector | ||
|
||
#### One attribute | ||
|
||
```elixir | ||
with_attr("button", "disabled") | ||
with_attr("input", "type", "checkbox") | ||
``` | ||
|
||
#### Several attributes | ||
|
||
```elixir | ||
with_attrs("input", type: "checkbox", name: "form[field]") | ||
``` | ||
|
||
#### Checking an element does not have a selector. | ||
|
||
```elixir | ||
without_attr("button", "disabled") | ||
without_attr("button", "phx-click", "remove-item") | ||
``` | ||
|
||
### Multiple selector options | ||
|
||
#### Checking just the existence of some attributes | ||
|
||
If the value given to one attribute is just `true`, it'll just check for the existence without checking for the value. | ||
|
||
```elixir | ||
with_attrs("button", type: "button", disabled: true) | ||
``` | ||
|
||
#### Checking that an attribute is not there | ||
|
||
We can also check that an attribute is not there with `with_attrs`, just give `false` as value. | ||
|
||
```elixir | ||
with_attrs("button", type: "button", disabled: false) | ||
``` | ||
|
||
### Use different matchers in attributes. | ||
|
||
`dom_helpers` support _"modifiers"_ in the form of `{modifier, value}` that | ||
can be used to change the matcher used in the selectors. [Documentation for these | ||
selectors can be found in MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors#syntax) | ||
|
||
#### Exact match | ||
|
||
Without a _"modifier"_, it just check for the exact value. There is also the `:equal` | ||
modifier. | ||
|
||
```elixir | ||
with_attr("input", "type", "checkbox") | ||
with_attr("input", "type", {:equal, "checkbox"}) | ||
with_attrs("input", type: "checkbox") | ||
with_attrs("input", type: {:equal, "checkbox"}) | ||
``` | ||
|
||
#### Contains | ||
|
||
Checks if the value given to the selector is contained within the actual value | ||
in the element. | ||
|
||
``` | ||
with_attr("input", "name", {:contains, "[certain_sub_field]"}) | ||
with_attrs("input", name: {:contains, "[certain_sub_field]"}) | ||
``` | ||
|
||
#### Contains word | ||
|
||
With the `:contains_word` modifier, the `~=` matcher is used. This matcher | ||
considers the value a list of whitespace-separated words and just checks that | ||
one of the words is the given value to the selector. | ||
|
||
```elixir | ||
with_attr("span", "class", {:contains_word, "hidden"}) | ||
with_attrs("span", class: {:contains_word, "hidden"}) | ||
|
||
# Yeah, I know it would be best to use `with_class` for those examples. | ||
``` | ||
|
||
#### Starts with / Ends with modifiers. | ||
|
||
There are also a `:starts_with` modifier for the `^=` matcher and `:ends_with` modifier for | ||
the `$=` matcher. | ||
|
||
```elixir | ||
with_attr("input", "name", {:starts_with, "form[field]"}) | ||
with_attrs("input", name: {:starts_with, "form[field]"}) | ||
with_attr("input", "name", {:ends_with, "[subfield][sub_subfield]"}) | ||
with_attrs("input", name: {:ends_with, "[subfield][sub_subfield]"}) | ||
``` | ||
|
||
#### Subcode | ||
|
||
See MDN documentation for this one, as it is a bit tricky. | ||
|
||
```elixir | ||
with_attr("html", "lang", {:subcode, "es"}) | ||
with_attrs("html", lang: {:subcode, "es"}) | ||
``` |
Oops, something went wrong.