Skip to content

Commit b5199f9

Browse files
committed
Docs: Verify Markup page first parts
1 parent 528573f commit b5199f9

File tree

11 files changed

+2208
-21
lines changed

11 files changed

+2208
-21
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<table>
2+
<caption>Lorem lipsum captium</caption>
3+
<tbody>
4+
<tr>
5+
<td style="white-space:nowrap">Foo</td>
6+
<td>Bar</td>
7+
</tr>
8+
<tr>
9+
<td style="white-space:nowrap">Baz</td>
10+
<td>Boo</td>
11+
</tr>
12+
</tbody>
13+
</table>
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<h3 id="heading-1337" required>
22
Heading text
3-
<small class="text-muted mark">Secondary text</small>
3+
<small class="text-muted mark">
4+
Secondary text
5+
</small>
46
</h3>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using Xunit;
2+
using Bunit;
3+
using System.Collections.Generic;
4+
using Microsoft.AspNetCore.Components;
5+
using Microsoft.AspNetCore.Components.Web;
6+
7+
using static Bunit.ComponentParameterFactory;
8+
9+
namespace Bunit.Docs.Samples
10+
{
11+
public class VerifyMarkupExamples
12+
{
13+
[Fact]
14+
public void RawMarkupVerify()
15+
{
16+
using var ctx = new TestContext();
17+
18+
var cut = ctx.RenderComponent<HelloWorld>();
19+
20+
var renderedMarkup = cut.Markup;
21+
Assert.Equal("<h1>Hello world from Blazor</h1>", renderedMarkup);
22+
}
23+
24+
[Fact]
25+
public void MarkupMatchesOnRenderedFragment()
26+
{
27+
using var ctx = new TestContext();
28+
29+
var cut = ctx.RenderComponent<Heading>();
30+
31+
cut.MarkupMatches(@"<h3 id=""heading-1337"" required>
32+
Heading text
33+
<small class=""mark text-muted"">Secondary text</small>
34+
</h3>");
35+
}
36+
37+
[Fact]
38+
public void MarkupMatchesOnNode()
39+
{
40+
using var ctx = new TestContext();
41+
42+
var cut = ctx.RenderComponent<Heading>();
43+
44+
var smallElm = cut.Find("small");
45+
smallElm.MarkupMatches(@"<small class=""mark text-muted"">Secondary text</small>");
46+
}
47+
48+
[Fact]
49+
public void MarkupMatchesOnTextNode()
50+
{
51+
using var ctx = new TestContext();
52+
53+
var cut = ctx.RenderComponent<Heading>();
54+
55+
var smallElmText = cut.Find("small").TextContent;
56+
smallElmText.MarkupMatches("Secondary text");
57+
}
58+
59+
[Fact]
60+
public void FindAndFindAll()
61+
{
62+
using var ctx = new TestContext();
63+
64+
var cut = ctx.RenderComponent<FancyTable>();
65+
66+
var tableCaption = cut.Find("caption");
67+
var tableCells = cut.FindAll("td:first-child");
68+
69+
Assert.Empty(tableCaption.Attributes);
70+
Assert.Equal(2, tableCells.Count);
71+
Assert.All(tableCells, td => td.HasAttribute("style"));
72+
}
73+
}
74+
}

docs/site/docs/verification/index.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ title: Verifying Output from a Component Under Test
55

66
# Verifying Output from a Component Under Test
77

8-
providing a toc with descriptions of each topic
8+
These section covers the different ways to verify the result of a test scenario:
9+
10+
- **<xref:verify-markup>:** This covers the different ways bUnit enables verification and assertions against the rendered markup from a component.
11+
- **<xref:verify-component-state>:** This covers how to inspect instance of the component under test.
12+
- **<xref:semantic-html-comparison>:** This covers how to customize the semantic HTML/markup comparer included in bUnit, for more stable tests.
13+
- **<xref:async-assertion>:** This covers how to create stable tests in an asynchronous world.

docs/site/docs/verification/semantic-html-comparison.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,16 @@ To verify the rendered output of a component, we have the `MarkupMatches()` meth
101101

102102
If for example we have a component, `<Heading>`, that renders the following markup:
103103

104-
[!code-html[Heading.razor](../../../samples/components/Heading.razor)]
104+
[!code-razor[Heading.razor](../../../samples/components/Heading.razor)]
105105

106106
If we want to verify the markup is rendered correctly, and for example use RegEx to verify the `id` attribute (it might be generated) and ignore the `<small>` element, we can do it like this in C# based tests:
107107

108108
[!code-csharp[SemanticHtmlTest.cs](../../../samples/tests/xunit/SemanticHtmlTest.cs#L16-L29)]
109109

110110
In a Razor based test, using the `<Fixture>` test type, the example looks like this:
111111

112-
[!code-html[SemanticHtmlTest.razor](../../../samples/tests/razor/SemanticHtmlTest.razor#L3-L30)]
112+
[!code-razor[SemanticHtmlTest.razor](../../../samples/tests/razor/SemanticHtmlTest.razor#L3-L30)]
113113

114114
In a Razor based test, using the `<SnapshotTest>` test type, the example looks like this:
115115

116-
[!code-html[SemanticHtmlTest.razor](../../../samples/tests/razor/SemanticHtmlTest.razor#L32-L42)]
116+
[!code-razor[SemanticHtmlTest.razor](../../../samples/tests/razor/SemanticHtmlTest.razor#L32-L42)]

docs/site/docs/verification/verify-component-state.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ Show Instance prop
1111

1212
Warn against setting props directly
1313

14+
FindComponent/FindComponents
Lines changed: 113 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,129 @@
11
---
22
uid: verify-markup
3-
title: Verifying the Rendered Markup from a Component Under Test
3+
title: Verifying Markup from a Component
44
---
55

6-
# Verifying the Rendered Markup from a Component Under Test
6+
# Verifying Markup from a Component
77

8-
Describe how markup is available.
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.
99

10-
List properties in renderedfragment for accessing rendered markup.
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.
1112
12-
Mention AngleSharp and the complete DOM api.
13+
This page cover the following **verification approaches**:
1314

14-
## Finding DOM Nodes
15+
- Basic verification of raw markup.
16+
- Semantic comparison of markup.
17+
- Inspecting the individual DOM nodes in the DOM tree.
18+
- Diffing of markup between renders.
1519

16-
### Refreshable Find Queries
20+
The following sections will cover each of these.
1721

18-
## Useful DOM Properties and Methods for Asserting
22+
## Basic Verification of Raw Markup
1923

20-
## Diffing DOM Nodes
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`.
2125

22-
- Since first render
23-
- since snapshot
24-
- Assertion helpers for List of IDiff
26+
To get the markup as a string, do the following:
27+
28+
[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=16&end=21&highlight=5)]
29+
30+
You can perform standard string assertions against the markup string, e.g. like checking if it contains a value or is empty.
31+
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.
2534
2635
## Semantic Comparison of Markup
2736

28-
- MarkupMatches
29-
- Make case for semantic comparison
30-
- Show use cases
37+
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.
38+
39+
### How does the Semantic HTML Comparer Work?
40+
41+
The comparer takes two HTML fragments (e.g. in the form of a C# string) as input, and returns `true` if both HTML fragments will result in the same visual rendered output in a web browser, otherwise it returns `false`.
42+
43+
For example, a web browser will render this HTML:
44+
45+
```html
46+
<span>Foo Bar</span>
47+
```
48+
49+
Exactly the same as this HTML:
50+
51+
```html
52+
<span>
53+
Foo Bar
54+
</span>
55+
```
56+
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.
58+
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.**
60+
61+
### The MarkupMatches() Method
62+
63+
The HTML comparer can be easily accessed through `MarkupMatches()` extension methods, available in places that represents HTML fragments in bUnit, i.e. on <xref:Bunit.IRenderedFragment> and the `INode` and `INodeList` types.
64+
65+
In the following examples, the `<Heading>` component listed below will be used as the component under test.
66+
67+
[!code-razor[Heading.razor](../../../samples/components/Heading.razor)]
68+
69+
To use the `MarkupMatches()` method to perform a semantic comparison of the output of the `<Heading>` component, through its <xref:Bunit.IRenderedFragment>, do the following:
70+
71+
[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=27&end=34&highlight=5-9)]
72+
73+
The highlighted line shows the call to the `MarkupMatches()` method. This test passes even though the insignificant whitespace is not exactly the same between the expected HTML string and the raw markup produced by the `<Heading>` component. It even works when the CSS class-list is not in the same order on the `<small>` element.
74+
75+
The `MarkupMatches()` method is also available on `INode` and `INodeList` types, for example:
76+
77+
[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=40&end=45&highlight=5-6)]
78+
79+
Here we use the `Find(string cssSelector)` method to find the `<small>` element, and only verify it and it's content and attributes.
80+
81+
> [!TIP]
82+
> Working with `Find()`, `FindAll()`, `INode` and `INodeList` is covered later on this page.
83+
84+
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:
3185

32-
Ref to customizing markup matches
86+
[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=51&end=56&highlight=5)]
87+
88+
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.
89+
90+
## Inspecting DOM Nodes
91+
92+
The rendered markup from a component is available as a DOM nodes through the <xref:Bunit.IRenderedFragment.Nodes> property on <xref:Bunit.IRenderedFragment>, as well as the `Find(string cssSelector)` and `FindAll(string cssSelector)` extension methods on <xref:Bunit.IRenderedFragment>.
93+
94+
The <xref:Bunit.IRenderedFragment.Nodes> property and the `FindAll()` method returns an [AngleSharp](https://anglesharp.github.io/) `INodeList` type, and the `Find()` method returns an [AngleSharp](https://anglesharp.github.io/) `IElement` type.
95+
96+
The DOM API in AngleSharp follows the W3C DOM API specifications and gives you the same results as state of the art browsers implementation of the DOM API in JavaScript does. Besides the official DOM API, AngleSharp and bUnit adds some useful extension methods on top. This makes working with DOM nodes convenient.
97+
98+
### Finding Nodes with the Find() and FindAll() methods
99+
100+
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+
102+
- [`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.
104+
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.
106+
107+
[!code-razor[FanyTable.razor](../../../samples/components/FanyTable.razor)]
108+
109+
To find the `<caption>` element and the first `<td>` elements in each row, do the following:
110+
111+
[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=62&end=67&highlight=5-6)]
112+
113+
Once you have one or more elements, you verify against them by e.g. inspecting their properties through the DOM API. For example:
114+
115+
[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=69&end=71)]
116+
117+
118+
119+
120+
121+
#### Refreshable FindAll() Queries
122+
123+
### Useful DOM Properties and Methods for Asserting
124+
125+
## Diffing DOM Nodes
126+
127+
- Since first render
128+
- since snapshot
129+
- Assertion helpers for List of IDiff
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
22

33
<script type="text/javascript" src="{{_rel}}styles/docfx.vendor.js"></script>
4+
<script type="text/javascript" src="{{_rel}}styles/cshtml-razor.js"></script>
5+
<script>
6+
hljs.registerLanguage('cshtml-razor', window.hljsDefineCshtmlRazor);
7+
</script>
48
<script type="text/javascript" src="{{_rel}}styles/docfx.js"></script>
59
<script type="text/javascript" src="{{_rel}}styles/main.js"></script>

0 commit comments

Comments
 (0)