-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathPrettyHtmlExtensions.cs
More file actions
150 lines (133 loc) · 3.78 KB
/
PrettyHtmlExtensions.cs
File metadata and controls
150 lines (133 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
using AngleSharp.Diffing;
using AngleSharp.Html;
using AngleSharp.Html.Parser;
using AwesomeAssertions;
using DiffPlex.DiffBuilder;
using DiffPlex.DiffBuilder.Model;
using JetBrains.Annotations;
using Xunit.Internal;
using Xunit.Sdk;
namespace Elastic.Markdown.Tests;
public static class PrettyHtmlExtensions
{
public static string PrettyHtml([LanguageInjection("html")] this string html, bool sanitize = true)
{
var parser = new HtmlParser();
var document = parser.ParseDocument(html);
var element = document.Body;
if (element is null)
return string.Empty;
if (sanitize)
{
var links = element.QuerySelectorAll("a");
links
.ForEach(l =>
{
l.RemoveAttribute("hx-get");
l.RemoveAttribute("hx-select-oob");
l.RemoveAttribute("hx-swap");
l.RemoveAttribute("hx-indicator");
l.RemoveAttribute("hx-push-url");
l.RemoveAttribute("preload");
});
}
using var sw = new StringWriter();
var formatter = new PrettyMarkupFormatter();
element.Children
.ForEach(c =>
{
// ReSharper disable once AccessToDisposedClosure
c.ToHtml(sw, formatter);
});
return sw.ToString().TrimStart('\n');
}
public static void ShouldBeHtml(
[LanguageInjection("html")] this string actual,
[LanguageInjection("html")] string expected,
bool sanitize = true
)
{
expected = expected.Trim('\n').PrettyHtml(sanitize);
actual = actual.Trim('\n').PrettyHtml(sanitize);
var diff = DiffBuilder
.Compare(actual)
.WithTest(expected)
.Build()
.ToArray();
if (diff.Length == 0)
return;
throw new XunitException(CreateDiff(actual, expected, sanitize));
}
public static void ShouldContainHtml(
[LanguageInjection("html")] this string actual,
[LanguageInjection("html")] string expected,
bool sanitize = true
)
{
expected = expected.Trim('\n').PrettyHtml(sanitize);
actual = actual.Trim('\n').PrettyHtml(sanitize);
var actualCompare = actual.Replace("\t", string.Empty);
var expectedCompare = expected.Replace("\t", string.Empty);
// we compare over unindented HTML, but if that fails, we rely on the pretty HTML Contain().
// to throw for improved error messages
if (!actualCompare.Contains(expectedCompare))
actual.Should().Contain(expected);
}
public static string CreateDiff(this string actual, string expected, bool sanitize = true)
{
expected = expected.Trim('\n').PrettyHtml(sanitize);
actual = actual.Trim('\n').PrettyHtml(sanitize);
var diffLines = InlineDiffBuilder.Diff(expected, actual).Lines;
var mutatedCount =
diffLines
.Count(l => l.Type switch
{
ChangeType.Unchanged => false,
ChangeType.Deleted => true,
ChangeType.Inserted => true,
ChangeType.Imaginary => false,
ChangeType.Modified => true,
_ => false
});
if (mutatedCount == 0)
return string.Empty;
var actualLineLength = actual.Split("\n").Length;
if (mutatedCount >= actualLineLength)
{
return $$"""
Mutations {{mutatedCount}} on all {{actualLineLength}} showing
EXPECTED:
{{expected}}
ACTUAL:
{{actual}}
""";
}
using var sw = new StringWriter();
diffLines
.ForEach(l =>
{
switch (l.Type)
{
case ChangeType.Unchanged:
sw.WriteLine(l.Text);
break;
case ChangeType.Deleted:
sw.WriteLine("- " + l.Text);
break;
case ChangeType.Inserted:
sw.WriteLine("+ " + l.Text);
break;
case ChangeType.Imaginary:
sw.WriteLine("? " + l.Text);
break;
case ChangeType.Modified:
sw.WriteLine("+ " + l.Text);
break;
}
});
return sw.ToString();
}
}