You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/site/docs/verification/verify-markup.md
+76-19Lines changed: 76 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,30 +8,32 @@ title: Verifying Markup from a Component
8
8
When a component is rendered in a test, the result is a <xref:Bunit.IRenderedFragment> or a <xref:Bunit.IRenderedComponent`1>. Through these it is possible to access the rendered markup (HTML) of the component, and in the case of <xref:Bunit.IRenderedComponent`1>, the instance of the component.
9
9
10
10
> [!NOTE]
11
-
> An <xref:Bunit.IRenderedComponent`1> inherits from <xref:Bunit.IRenderedFragment>. This page will only show how a <xref:Bunit.IRenderedFragment>is used. <xref:Bunit.IRenderedComponent`1> is covered on the <xref:verify-component-state> page.
11
+
> An <xref:Bunit.IRenderedComponent`1> inherits from <xref:Bunit.IRenderedFragment>. This page will only cover features of the <xref:Bunit.IRenderedFragment>type. <xref:Bunit.IRenderedComponent`1> is covered on the <xref:verify-component-state> page.
12
12
13
-
This page cover the following **verification approaches**:
13
+
This page cover the following **verification approaches:**
14
14
15
15
- Basic verification of raw markup.
16
16
- Semantic comparison of markup.
17
17
- Inspecting the individual DOM nodes in the DOM tree.
18
-
-Diffing of markup between renders.
18
+
-Finding expected differences in markup between renders.
19
19
20
20
The following sections will cover each of these.
21
21
22
22
## Basic Verification of Raw Markup
23
23
24
24
To access the rendered markup of a component, just use the <xref:Bunit.IRenderedFragment.Markup> property on <xref:Bunit.IRenderedFragment>. It holds the *raw* HTML from the component as a `string`.
25
25
26
+
> [!WARNING]
27
+
> Be aware that all indentions and whitespace in your components (`.razor` files) are included in the raw rendered markup, so it is often wise to normalize the markup string a little, e.g. via the string `Trim()` method, to make the tests more stable. Otherwise a change to the formatting in your components might break the tests when it does not need to.
28
+
>
29
+
> To avoid these issues and others related to asserting against raw markup, use the semantic HTML comparer that comes with bUnit, described in the next section.
You can perform standard string assertions against the markup string, e.g. like checking if it contains a value or is empty.
31
36
32
-
> [!WARNING]
33
-
> Be aware that all indentions and whitespace in your components (`.razor` files) are included in the rendered markup, so it is often wise to normalize the markup string a little, e.g. via the string `Trim()` method, to make the tests more stable. Otherwise a change to the formatting in your components might break the tests when it does not need to.
34
-
35
37
## Semantic Comparison of Markup
36
38
37
39
Working with raw markup only works well with very simple output, and even then, you have to sanitize it to get stable tests. A much better approach is to use the semantic HTML comparer that comes with bUnit.
@@ -50,13 +52,13 @@ Exactly the same as this HTML:
50
52
51
53
```html
52
54
<span>
53
-
Foo Bar
55
+
Foo Bar
54
56
</span>
55
57
```
56
58
57
-
That is why it makes sense to allow tests to pass, _even_ when the rendered HTML markup is not entire identical to the expected HTML, from a normal string comparer perspective.
59
+
That is why it makes sense to allow tests to pass, _even_ when the rendered HTML markup is not entirely identical to the expected HTML, from a normal string comparer's perspective.
58
60
59
-
bUnit's semantic HTML comparer safely ignores things like insignificant whitespace and the order of attributes on elements, and many more things. **This leads to much more stable tests, as e.g. a reformatted component doesn't break it's tests because insignificant whitespace changes.**
61
+
bUnit's semantic HTML comparer safely ignores things like insignificant whitespace and the order of attributes on elements, and many more things. **This leads to much more stable tests, as e.g. a reformatted component doesn't break it's tests because of insignificant whitespace changes.**
60
62
61
63
### The MarkupMatches() Method
62
64
@@ -81,11 +83,15 @@ Here we use the `Find(string cssSelector)` method to find the `<small>` element,
81
83
> [!TIP]
82
84
> Working with `Find()`, `FindAll()`, `INode` and `INodeList` is covered later on this page.
83
85
86
+
<!-- TODO UNCOMMENT WHEN MarkupMatches(this string markup ... ) IS DONE
84
87
Text content can also be verified with the `MarkupMatches()` method, e.g. the text inside the `<small>` element. It has the advantage over regular string comparison that it removes insignificant whitespace in the text automatically, even between words, where a normal string `Trim()` method isn't enough. For example:
The semantic HTML comparer can be customized to make a test case even more stable and easier to maintain. It is e.g. possible to ignore an element or attribute during comparison, or provide an regular expression to the comparer when comparing a specific element or attribute, to make the comparer work with generated data. Learn more about the customizations options on the <xref:semantic-html-comparison> page.
92
+
The semantic HTML comparer can be customized to make a test case even more stable and easier to maintain. It is e.g. possible to ignore an element or attribute during comparison, or provide an regular expression to the comparer when comparing a specific element or attribute, to make the comparer work with generated data.
93
+
94
+
Learn more about the customizations options on the <xref:semantic-html-comparison> page.
89
95
90
96
## Inspecting DOM Nodes
91
97
@@ -100,11 +106,11 @@ The DOM API in AngleSharp follows the W3C DOM API specifications and gives you t
100
106
Users of the famous JavaScript framework [jQuery](https://jquery.com/) will recognize the two methods [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)).
101
107
102
108
-[`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) takes a "CSS selector" as input and returns an `IElement` as output, or throws an exception if non is found.
103
-
-[`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) takes a "CSS selector" as input a list of `IElement` elements.
109
+
-[`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) takes a "CSS selector" as input and returns a list of `IElement` elements.
104
110
105
-
Let's see some examples of using the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) methods to query the `<FanyTable>` component listed below.
111
+
Let's see some examples of using the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) methods to query the `<FancyTable>` component listed below.
A element found with the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) method will be updated if the component it came from is re-rendered.
126
+
127
+
However, that does not apply to elements that are found by traversing the DOM tree via e.g. the <xref:Bunit.IRenderedFragment.Nodes> property on <xref:Bunit.IRenderedFragment>, as those nodes do not know when their root component is re-rendered, and thus, when they should be updated.
128
+
129
+
Therefore, always prefer using the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) method when searching for a single element, or always reissue the query whenever you need the element.
130
+
131
+
#### Auto-refreshable FindAll() Queries
132
+
133
+
The [`FindAll(string cssSelector, bool enableAutoRefresh = false)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) method has an optional parameter, `enableAutoRefresh`, which, when set to `true`, will return an collection of `IElement`, that automatically refreshes itself when the component the elements came from is re-rendered.
134
+
135
+
## Finding Expected Differences
136
+
137
+
It can sometimes be easier to verify that an expected change has happened in the rendered markup (and only that), than it can be to specify how all the rendered markup should look after re-render.
138
+
139
+
bUnit comes with a number of ways for finding lists of `IDiff`, the representation of a difference between two HTML fragments. All of these are direct methods or extension methods on the <xref:Bunit.IRenderedFragment> type, or on the `INode` or `INodeList` types:
140
+
141
+
-<xref:Bunit.IRenderedFragment.GetChangesSinceFirstRender> method on <xref:Bunit.IRenderedFragment>. This method returns a list of differences since the initial first render of a component.
142
+
-<xref:Bunit.IRenderedFragment.GetChangesSinceSnapshot> and <xref:Bunit.IRenderedFragment.SaveSnapshot> methods on <xref:Bunit.IRenderedFragment>. These two methods combined makes it possible to get a list of differences between the last time the <xref:Bunit.IRenderedFragment.SaveSnapshot> method was called and a call to the <xref:Bunit.IRenderedFragment.GetChangesSinceSnapshot> method is placed.
143
+
-`CompareTo()` methods from <xref:Bunit.CompareToExtensions> for the <xref:Bunit.IRenderedFragment>, `INode`, and `INodeList` types. These methods returns a list of differences between the two input HTML fragments.
144
+
145
+
In addition to this, there are a number of experimental assertion helpers for `IDiff` and `IEnumerable<IDiff>`, that makes it easier and more concise to declare your assertions.
146
+
147
+
Let's look at a few examples. In the first we will use the `<Counter>` component listed below:
- On line 8, <xref:Bunit.IRenderedFragment.GetChangesSinceFirstRender> is used to get a list of differences.
158
+
- On line 11, the [`ShouldHaveSingleChange()`](xref:Bunit.DiffAssertExtensions.ShouldHaveSingleChange(System.Collections.Generic.IEnumerable{AngleSharp.Diffing.Core.IDiff})) method is used to verify that there is only one change found.
159
+
- On line 14, the [`ShouldBeTextChange()`](xref:Bunit.ShouldBeTextChangeAssertExtensions.ShouldBeTextChange(AngleSharp.Diffing.Core.IDiff,System.String,System.String)) method is used to verify that the single `IDiff` is a text change.
160
+
161
+
Testing a more **complex life cycle of a component** can be more easily done using the <xref:Bunit.IRenderedFragment.GetChangesSinceSnapshot> and <xref:Bunit.IRenderedFragment.SaveSnapshot> methods, along with a host of other assert helpers.
162
+
163
+
This example tests the `<CheckList>` component listed below. The component allow you to add new items to the check list by typing into the input field and hitting the `enter` key, and items can be removed again from the list by clicking on them.
### Useful DOM Properties and Methods for Asserting
171
+
This is what happens in the test:
120
172
121
-
## Diffing DOM Nodes
173
+
1. First the component is rendered and the input field is found.
174
+
2. The the first item is added through the input field.
175
+
3. The <xref:Bunit.IRenderedFragment.GetChangesSinceFirstRender>, [`ShouldHaveSingleChange()`](xref:Bunit.DiffAssertExtensions.ShouldHaveSingleChange(System.Collections.Generic.IEnumerable{AngleSharp.Diffing.Core.IDiff})) and [`ShouldBeAddition()`](xref:Bunit.ShouldBeAdditionAssertExtensions.ShouldBeAddition(AngleSharp.Diffing.Core.IDiff,System.String,System.String)) methods are used to verify that the item was correctly added.
176
+
4. The <xref:Bunit.IRenderedFragment.SaveSnapshot> is used to save a snapshot of current DOM nodes internally in the `cut`. This reduces the number of diffs found in the following steps, simplifying verification.
177
+
5. A second item is added to the check list.
178
+
6. Two verifications is performed at this point, one using the <xref:Bunit.IRenderedFragment.GetChangesSinceFirstRender> method which finds two changes, and one using the <xref:Bunit.IRenderedFragment.GetChangesSinceSnapshot> method, that finds a single change. The first is only done for illustrative purposes.
179
+
7. A new snapshot is saved, replacing the previous one, with a another call to the <xref:Bunit.IRenderedFragment.SaveSnapshot> method.
180
+
8. Finally the last item in the list is found and clicked, and the <xref:Bunit.IRenderedFragment.GetChangesSinceSnapshot> method is used to find the changes, a single diff, which is verified as a removal of the second item.
122
181
123
-
- Since first render
124
-
- since snapshot
125
-
- Assertion helpers for List of IDiff
182
+
As mentioned earlier, the `IDiff` assertion helpers are still experimental. Any feedback and suggestions for improvements should be directed to the [related issue](https://github.com/egil/bUnit/issues/84) on GitHub.
0 commit comments