Skip to content

Commit c1a8a5a

Browse files
committed
♻️ refactoring and adding a few more tests
1 parent 949310e commit c1a8a5a

File tree

3 files changed

+55
-21
lines changed

3 files changed

+55
-21
lines changed

src/HtmlDiff.Tests/UtilsTests.cs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using NExpect;
2+
using NUnit.Framework;
3+
using static NExpect.Expectations;
4+
5+
namespace HtmlDiff.Tests
6+
{
7+
[TestFixture]
8+
public class UtilsTests
9+
{
10+
[TestFixture]
11+
public class GetTagName
12+
{
13+
[TestCase("", "")]
14+
[TestCase(null, "")]
15+
[TestCase("test", "")]
16+
[TestCase("<test", "")]
17+
[TestCase("< div >", "")]
18+
[TestCase("</ div>", "")]
19+
[TestCase("<div>", "div")]
20+
[TestCase(" \t<div> \t", "div")]
21+
[TestCase("<DIV>", "div")]
22+
[TestCase("</div>", "div")]
23+
[TestCase("<div attr='test'>", "div")]
24+
[TestCase("<div attr=test>", "div")]
25+
public void WithTestCase_ShouldMapToTagName(string input, string expected)
26+
{
27+
// arrange and act
28+
var result = Utils.GetTagName(input);
29+
30+
// assert
31+
Expect(result).To.Equal(expected);
32+
}
33+
}
34+
}
35+
}

src/HtmlDiff/HtmlDiff.cs

+9-13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public class HtmlDiff
1212
/// This value defines balance between speed and memory utilization. The higher it is the faster it works and more memory consumes.
1313
/// </summary>
1414
private const int MatchGranularityMaximum = 4;
15+
private const string DelTag = "del";
16+
private const string InsTag = "ins";
1517

1618
private readonly StringBuilder _content;
1719
private string _newText;
@@ -188,13 +190,13 @@ private void ProcessReplaceOperation(Operation operation)
188190
private void ProcessInsertOperation(Operation operation, string cssClass)
189191
{
190192
var text = _newWords.Where((s, pos) => pos >= operation.StartInNew && pos < operation.EndInNew).ToList();
191-
InsertTag("ins", cssClass, text);
193+
InsertTag(InsTag, cssClass, text);
192194
}
193195

194196
private void ProcessDeleteOperation(Operation operation, string cssClass)
195197
{
196198
var text = _oldWords.Where((s, pos) => pos >= operation.StartInOld && pos < operation.EndInOld).ToList();
197-
InsertTag("del", cssClass, text);
199+
InsertTag(DelTag, cssClass, text);
198200
}
199201

200202
private void ProcessEqualOperation(Operation operation)
@@ -247,12 +249,11 @@ private void InsertTag(string tag, string cssClass, List<string> words)
247249
}
248250
else
249251
{
250-
// Check if the tag is a special case
251252
if (SpecialCaseOpeningTagRegex.IsMatch(words[0]))
252253
{
253254
_specialTagDiffStack.Push(words[0]);
254255
specialCaseTagInjection = "<ins class='mod'>";
255-
if (tag == "del")
256+
if (tag == DelTag)
256257
{
257258
words.RemoveAt(0);
258259

@@ -263,23 +264,18 @@ private void InsertTag(string tag, string cssClass, List<string> words)
263264
}
264265
}
265266
}
266-
267267
else if (_specialCaseClosingTags.ContainsKey(words[0]))
268268
{
269269
var openingTag = _specialTagDiffStack.Count == 0 ? null : _specialTagDiffStack.Pop();
270-
271-
// If we didn't have an opening tag, and we don't have a match with the previous tag used
272-
if (openingTag == null || Utils.GetTagName(openingTag) != Utils.GetTagName(words.Last()))
273-
{
274-
// do nothing
275-
}
276-
else
270+
var hasOpeningTag = !(openingTag is null);
271+
var openingAndClosingTagsMatch = Utils.GetTagName(openingTag) == Utils.GetTagName(words.Last());
272+
if (hasOpeningTag && openingAndClosingTagsMatch)
277273
{
278274
specialCaseTagInjection = "</ins>";
279275
specialCaseTagInjectionIsBefore = true;
280276
}
281277

282-
if (tag == "del")
278+
if (tag == DelTag)
283279
{
284280
words.RemoveAt(0);
285281

src/HtmlDiff/Utils.cs

+11-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public static class Utils
1111
private static Regex tagWordRegex = new Regex(@"<[^\s>]+", RegexOptions.Compiled);
1212
private static Regex whitespaceRegex = new Regex("^(\\s|&nbsp;)+$", RegexOptions.Compiled);
1313
private static Regex wordRegex = new Regex(@"[\w\#@]+", RegexOptions.Compiled | RegexOptions.ECMAScript);
14-
private static Regex tagRegex = new Regex(@"\s*</?(?<name>[^\s>]+)[^>]*>\s*", RegexOptions.Compiled);
14+
private static Regex tagRegex = new Regex(@"</?(?<name>[^\s/>]+)[^>]*>", RegexOptions.Compiled);
1515

1616
private static readonly string[] SpecialCaseWordTags = { "<img" };
1717

@@ -79,6 +79,7 @@ public static string StripAnyAttributes(string word)
7979
{
8080
return StripTagAttributes(word);
8181
}
82+
8283
return word;
8384
}
8485

@@ -89,13 +90,15 @@ public static bool IsWord(char text)
8990

9091
public static string GetTagName(string word)
9192
{
92-
var match = tagRegex.Match(word);
93-
94-
// returns empty string if it's not a tag
95-
if (!match.Success)
96-
return string.Empty;
97-
98-
return match.Groups["name"].Value.ToLower();
93+
var noResult = string.Empty;
94+
if (word is null)
95+
{
96+
return noResult;
97+
}
98+
var tagMatch = tagRegex.Match(word);
99+
return tagMatch.Success
100+
? tagMatch.Groups["name"].Value.ToLower()
101+
: noResult;
99102
}
100103
}
101104
}

0 commit comments

Comments
 (0)