From 89c2621bf66c330dc3f5f025accef543f6fa59ee Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Wed, 7 Dec 2011 18:29:55 +0000 Subject: [PATCH] Add simple ASP.NET web forms fold parser. --- .../AspNet.Mvc/Project/AspNet.Mvc.csproj | 11 + .../Project/Src/Folding/CharacterReader.cs | 85 +++++ .../Project/Src/Folding/HtmlElementFold.cs | 56 +++ .../Project/Src/Folding/HtmlNode.cs | 37 ++ .../Src/Folding/SimpleWebFormsHtmlReader.cs | 111 ++++++ .../Src/Folding/WebFormsHtmlFoldParser.cs | 66 ++++ .../AspNet.Mvc/Test/AspNet.Mvc.Tests.csproj | 8 + .../Test/Src/Folding/HtmlElementFoldTests.cs | 105 ++++++ .../Folding/WebFormsHtmlFoldParserTests.cs | 159 +++++++++ .../Src/Folding/WebFormsHtmlReaderTests.cs | 324 ++++++++++++++++++ 10 files changed, 962 insertions(+) create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/CharacterReader.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlElementFold.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlNode.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/SimpleWebFormsHtmlReader.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/WebFormsHtmlFoldParser.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/HtmlElementFoldTests.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlFoldParserTests.cs create mode 100644 src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlReaderTests.cs diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.csproj b/src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.csproj index 3eb7704d3b..6fede1304a 100644 --- a/src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.csproj +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.csproj @@ -115,6 +115,11 @@ + + + + + @@ -198,8 +203,14 @@ + + + {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} + ICSharpCode.AvalonEdit + False + {2748AD25-9C63-4E12-877B-4DCE96FBED54} ICSharpCode.SharpDevelop diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/CharacterReader.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/CharacterReader.cs new file mode 100644 index 0000000000..74d0070082 --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/CharacterReader.cs @@ -0,0 +1,85 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.IO; + +namespace ICSharpCode.AspNet.Mvc.Folding +{ + public class CharacterReader + { + public const int EndOfCharacters = -1; + + TextReader reader; + + public CharacterReader(string html) + : this(new StringReader(html)) + { + } + + public CharacterReader(TextReader reader) + { + this.reader = reader; + } + + public bool Read() + { + CurrentCharacter = reader.Read(); + Offset++; + return HasMoreCharactersToRead(); + } + + bool HasMoreCharactersToRead() + { + return CurrentCharacter != EndOfCharacters; + } + + public int Offset { get; private set; } + + public int PreviousOffset { + get { return Offset - 1; } + } + + public int CurrentCharacter { get; private set; } + + public bool IsLetterOrDigit() + { + return Char.IsLetterOrDigit((char)CurrentCharacter); + } + + public bool IsLessThanSign() + { + return CurrentCharacter == '<'; + } + + public bool IsGreaterThanSign() + { + return CurrentCharacter == '>'; + } + + public bool IsForwardSlash() + { + return CurrentCharacter == '/'; + } + + public bool IsNextCharacterForwardSlash() + { + return reader.Peek() == '/'; + } + + public bool IsSpace() + { + return CurrentCharacter == ' '; + } + + public bool IsDoubleQuote() + { + return CurrentCharacter == '\"'; + } + + public bool IsSingleQuote() + { + return CurrentCharacter == '\''; + } + } +} diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlElementFold.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlElementFold.cs new file mode 100644 index 0000000000..c7f3bc876a --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlElementFold.cs @@ -0,0 +1,56 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.AvalonEdit.Folding; + +namespace ICSharpCode.AspNet.Mvc.Folding +{ + public class HtmlElementFold : NewFolding + { + string elementName = String.Empty; + + public HtmlElementFold() + { + } + + public string ElementName { + get { return elementName; } + set { + elementName = value; + UpdateFoldName(); + } + } + + void UpdateFoldName() + { + Name = String.Format("<{0}>", elementName); + } + + public override bool Equals(object obj) + { + var rhs = obj as HtmlElementFold; + if (rhs != null) { + return + (elementName == rhs.ElementName) && + (StartOffset == rhs.StartOffset) && + (EndOffset == rhs.EndOffset); + } + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return String.Format( + "[HtmlElementFold Name='{0}', StartOffset={1}, EndOffset={2}]", + Name, + StartOffset, + EndOffset); + } + } +} diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlNode.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlNode.cs new file mode 100644 index 0000000000..e7cc615f19 --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/HtmlNode.cs @@ -0,0 +1,37 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Text; + +namespace ICSharpCode.AspNet.Mvc.Folding +{ + public class HtmlNode + { + StringBuilder nodeValue = new StringBuilder(); + + public string Value { + get { return GetElementNameBeforeSpaceCharacter(); } + } + + string GetElementNameBeforeSpaceCharacter() + { + string elementName = nodeValue.ToString(); + return GetTextBeforeSpaceCharacter(elementName); + } + + string GetTextBeforeSpaceCharacter(string text) + { + int spaceIndex = text.IndexOf(' '); + if (spaceIndex > 0) { + return text.Substring(0, spaceIndex); + } + return text; + } + + public void Append(int character) + { + nodeValue.Append((char)character); + } + } +} diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/SimpleWebFormsHtmlReader.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/SimpleWebFormsHtmlReader.cs new file mode 100644 index 0000000000..cb921018e2 --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/SimpleWebFormsHtmlReader.cs @@ -0,0 +1,111 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.AspNet.Mvc.Folding +{ + public class SimpleWebFormsHtmlReader + { + CharacterReader reader; + HtmlNode currentNode = new HtmlNode(); + + public SimpleWebFormsHtmlReader(string html) + : this(new StringReader(html)) + { + } + + public SimpleWebFormsHtmlReader(TextReader reader) + : this(new CharacterReader(reader)) + { + } + + public SimpleWebFormsHtmlReader(CharacterReader reader) + { + this.reader = reader; + } + + public string Value { + get { return currentNode.Value; } + } + + public int Offset { get; private set; } + public int Length { get; private set; } + + public int EndOffset { + get { return Offset + Length; } + } + + public bool IsEmptyElement { get; private set; } + public bool IsEndElement { get; private set; } + + public bool IsStartElement { + get { return !IsEndElement; } + } + + public bool Read() + { + while (ReadNextCharacter()) { + if (IsElementStartCharacter()) { + currentNode = new HtmlNode(); + IsEndElement = reader.IsNextCharacterForwardSlash(); + IsEmptyElement = false; + Offset = reader.PreviousOffset; + } else if (IsElementEndCharacter()) { + Length = reader.Offset - Offset; + return true; + } else if (reader.IsForwardSlash()) { + IsEmptyElement = !IsEndElement; + } else if (IsElementNameCharacter()) { + currentNode.Append(reader.CurrentCharacter); + } else if (reader.IsDoubleQuote()) { + ReadDoubleQuotedString(); + } else if (reader.IsSingleQuote()) { + ReadSingleQuotedString(); + } + } + return false; + } + + bool ReadNextCharacter() + { + return reader.Read(); + } + + bool IsElementStartCharacter() + { + return reader.IsLessThanSign(); + } + + bool IsElementEndCharacter() + { + return reader.IsGreaterThanSign(); + } + + bool IsElementNameCharacter() + { + return reader.IsLetterOrDigit() || reader.IsSpace(); + } + + void ReadDoubleQuotedString() + { + ReadUntil(() => reader.IsDoubleQuote()); + } + + void ReadUntil(Func match) + { + while (ReadNextCharacter()) { + if (match()) { + return; + } + } + } + + void ReadSingleQuotedString() + { + ReadUntil(() => reader.IsSingleQuote()); + } + } +} diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/WebFormsHtmlFoldParser.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/WebFormsHtmlFoldParser.cs new file mode 100644 index 0000000000..236154e446 --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Folding/WebFormsHtmlFoldParser.cs @@ -0,0 +1,66 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.AvalonEdit.Folding; + +namespace ICSharpCode.AspNet.Mvc.Folding +{ + public class WebFormsHtmlFoldParser + { + List folds = new List(); + Stack foldStack = new Stack(); + SimpleWebFormsHtmlReader htmlReader; + + public IEnumerable GetFolds(string html) + { + ClearPreviousFolds(); + htmlReader = new SimpleWebFormsHtmlReader(html); + while (htmlReader.Read()) { + if (htmlReader.IsEmptyElement) { + // No folds for empty elements. + } else if (htmlReader.IsEndElement) { + AddFoldForCompletedElement(); + } else { + SaveFoldStartOnStack(); + } + } + SortFoldsByStartOffset(); + return folds; + } + + void ClearPreviousFolds() + { + folds.Clear(); + } + + void SaveFoldStartOnStack() + { + var fold = new HtmlElementFold() { + ElementName = htmlReader.Value, + StartOffset = htmlReader.Offset + }; + foldStack.Push(fold); + } + + void AddFoldForCompletedElement() + { + if (foldStack.Any()) { + var fold = foldStack.Pop(); + if (fold.ElementName == htmlReader.Value) { + fold.EndOffset = htmlReader.EndOffset; + folds.Add(fold); + } else { + AddFoldForCompletedElement(); + } + } + } + + void SortFoldsByStartOffset() + { + folds.Sort((fold1, fold2) => fold1.StartOffset.CompareTo(fold2.StartOffset)); + } + } +} diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Test/AspNet.Mvc.Tests.csproj b/src/AddIns/BackendBindings/AspNet.Mvc/Test/AspNet.Mvc.Tests.csproj index a7f03462ac..26486ef4fa 100644 --- a/src/AddIns/BackendBindings/AspNet.Mvc/Test/AspNet.Mvc.Tests.csproj +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Test/AspNet.Mvc.Tests.csproj @@ -103,6 +103,9 @@ + + + @@ -124,6 +127,10 @@ + + {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} + ICSharpCode.AvalonEdit + {2748AD25-9C63-4E12-877B-4DCE96FBED54} ICSharpCode.SharpDevelop @@ -149,6 +156,7 @@ + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/HtmlElementFoldTests.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/HtmlElementFoldTests.cs new file mode 100644 index 0000000000..936279eefd --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/HtmlElementFoldTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.AspNet.Mvc.Folding; +using NUnit.Framework; + +namespace AspNet.Mvc.Tests.Folding +{ + [TestFixture] + public class HtmlElementFoldTests + { + HtmlElementFold fold; + HtmlElementFold lhs; + HtmlElementFold rhs; + + void CreateHtmlElementFoldWithElementName(string elementName) + { + fold = new HtmlElementFold() { ElementName = elementName }; + } + + void CreateHtmlElementFoldsForComparison() + { + lhs = new HtmlElementFold(); + rhs = new HtmlElementFold(); + } + + [Test] + public void Name_HtmlFoldCreatedUsingElementName_ReturnsElementNameInsideTags() + { + CreateHtmlElementFoldWithElementName("p"); + + Assert.AreEqual("

", fold.Name); + } + + [Test] + public void Equals_AllPropertiesAreTheSame_ReturnsTrue() + { + CreateHtmlElementFoldsForComparison(); + + bool result = lhs.Equals(rhs); + + Assert.IsTrue(result); + } + + [Test] + public void Equals_NullCompared_ReturnsFalse() + { + CreateHtmlElementFoldsForComparison(); + + bool result = lhs.Equals(null); + + Assert.IsFalse(result); + } + + [Test] + public void Equals_DifferentElementNames_ReturnsFalse() + { + CreateHtmlElementFoldsForComparison(); + lhs.ElementName = "Test1"; + rhs.ElementName = "Test"; + + bool result = lhs.Equals(rhs); + + Assert.IsFalse(result); + } + + [Test] + public void Equals_DifferentStartOffsets_ReturnsFalse() + { + CreateHtmlElementFoldsForComparison(); + lhs.StartOffset = 0; + rhs.StartOffset = 100; + + bool result = lhs.Equals(rhs); + + Assert.IsFalse(result); + } + + [Test] + public void Equals_DifferentEndOffsets_ReturnsFalse() + { + CreateHtmlElementFoldsForComparison(); + lhs.EndOffset = 0; + rhs.EndOffset = 100; + + bool result = lhs.Equals(rhs); + + Assert.IsFalse(result); + } + + [Test] + public void ToString_ElementNameAndOffsetsSet_ReturnsFoldNameAndOffsets() + { + CreateHtmlElementFoldWithElementName("foo"); + fold.StartOffset = 1; + fold.EndOffset = 10; + + string text = fold.ToString(); + + string expectedText = "[HtmlElementFold Name='', StartOffset=1, EndOffset=10]"; + Assert.AreEqual(expectedText, text); + } + } +} diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlFoldParserTests.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlFoldParserTests.cs new file mode 100644 index 0000000000..fa12832051 --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlFoldParserTests.cs @@ -0,0 +1,159 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; + +using ICSharpCode.AspNet.Mvc.Folding; +using ICSharpCode.AvalonEdit.Folding; +using NUnit.Framework; + +namespace AspNet.Mvc.Tests.Folding +{ + [TestFixture] + public class WebFormsHtmlFoldParserTests + { + WebFormsHtmlFoldParser parser; + List folds; + + void CreateParser() + { + parser = new WebFormsHtmlFoldParser(); + } + + void GetFolds(string text) + { + folds = parser.GetFolds(text).ToList(); + } + + [Test] + public void GetFolds_ParagraphStartAndEndTag_ReturnsOneFoldForParagraphTag() + { + CreateParser(); + + string text = + "

\r\n" + + "

"; + + GetFolds(text); + + var expectedFolds = new HtmlElementFold[] { + new HtmlElementFold() { + ElementName = "p", + StartOffset = 0, + EndOffset = 9 + } + }; + + CollectionAssert.AreEqual(expectedFolds, folds); + } + + [Test] + public void GetFolds_HtmlWithSingleParagraphStartAndEndTag_ReturnsTwoFolds() + { + CreateParser(); + + string text = + "\r\n" + + "

\r\n" + + "

\r\n" + + ""; + + GetFolds(text); + + var expectedFolds = new HtmlElementFold[] { + new HtmlElementFold() { + ElementName = "html", + StartOffset = 0, + EndOffset = 26 + }, + new HtmlElementFold() { + ElementName = "p", + StartOffset = 8, + EndOffset = 17 + } + }; + + CollectionAssert.AreEqual(expectedFolds, folds); + } + + [Test] + public void GetFolds_GetFoldsCalledTwiceForParagraphStartAndEndTag_ReturnsOneFoldForParagraphTag() + { + CreateParser(); + + string text = + "

\r\n" + + "

"; + + GetFolds(text); + GetFolds(text); + + var expectedFolds = new HtmlElementFold[] { + new HtmlElementFold() { + ElementName = "p", + StartOffset = 0, + EndOffset = 9 + } + }; + + CollectionAssert.AreEqual(expectedFolds, folds); + } + + [Test] + public void GetFolds_ParagraphTagsWithUnclosedBoldTagInside_ReturnsOneFoldForParagraphTag() + { + CreateParser(); + + string text = + "

\r\n" + + "\r\n" + + "

"; + + GetFolds(text); + + var expectedFolds = new HtmlElementFold[] { + new HtmlElementFold() { + ElementName = "p", + StartOffset = 0, + EndOffset = 14 + } + }; + + CollectionAssert.AreEqual(expectedFolds, folds); + } + + [Test] + public void GetFolds_EndParagraphTag_ReturnsNoFolds() + { + CreateParser(); + + GetFolds("

"); + + Assert.AreEqual(0, folds.Count); + } + + [Test] + public void GetFolds_DivTagWithClassAttribute_ReturnsOneFold() + { + CreateParser(); + + string text = + "
\r\n" + + "
\r\n"; + + GetFolds(text); + + var expectedFolds = new HtmlElementFold[] { + new HtmlElementFold() { + ElementName = "div", + StartOffset = 0, + EndOffset = 25 + } + }; + + CollectionAssert.AreEqual(expectedFolds, folds); + } + } +} \ No newline at end of file diff --git a/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlReaderTests.cs b/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlReaderTests.cs new file mode 100644 index 0000000000..cc54d29a6b --- /dev/null +++ b/src/AddIns/BackendBindings/AspNet.Mvc/Test/Src/Folding/WebFormsHtmlReaderTests.cs @@ -0,0 +1,324 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.IO; +using ICSharpCode.AspNet.Mvc.Folding; +using NUnit.Framework; + +namespace AspNet.Mvc.Tests.Folding +{ + public class WebFormsHtmlReaderTests + { + SimpleWebFormsHtmlReader htmlReader; + + void CreateHtmlReader(string html) + { + htmlReader = new SimpleWebFormsHtmlReader(html); + } + + [Test] + public void Value_ReadSingleParagraphTag_ReturnsParagraphTagName() + { + CreateHtmlReader("

"); + htmlReader.Read(); + + string value = htmlReader.Value; + + Assert.AreEqual("p", value); + } + + [Test] + public void Read_EmptyString_ReturnsFalse() + { + CreateHtmlReader(String.Empty); + + bool result = htmlReader.Read(); + + Assert.IsFalse(result); + } + + [Test] + public void Read_ReadSingleParagraphTagHtml_ReturnsTrue() + { + CreateHtmlReader("

"); + + bool result = htmlReader.Read(); + + Assert.IsTrue(result); + } + + [Test] + public void Read_ReadTwiceFromSingleParagraphTag_SecondReadReturnsFalse() + { + CreateHtmlReader("

"); + + bool result = htmlReader.Read(); + result = htmlReader.Read(); + + Assert.IsFalse(result); + } + + [Test] + public void Offset_SingleParagraphTagAtStartOfString_ReturnsZero() + { + CreateHtmlReader("

"); + htmlReader.Read(); + + int offset = htmlReader.Offset; + + Assert.AreEqual(0, offset); + } + + [Test] + public void Length_SingleParagraphTagAtStartOfString_ReturnsThree() + { + CreateHtmlReader("

"); + htmlReader.Read(); + + int length = htmlReader.Length; + + Assert.AreEqual(3, length); + } + + [Test] + public void Offset_SingleParagraphTagOnSecondLine_IsAfterCarriageReturnAndLineFeedAndReturnsTwo() + { + CreateHtmlReader("\r\n

"); + htmlReader.Read(); + + int offset = htmlReader.Offset; + + Assert.AreEqual(2, offset); + } + + [Test] + public void Length_SingleParagraphTagOnSecondLine_ReturnsThreeForParagraphNodeRead() + { + CreateHtmlReader("\r\n

"); + htmlReader.Read(); + + int length = htmlReader.Length; + + Assert.AreEqual(3, length); + } + + [Test] + public void Value_ReadSingleLineBreakTag_ReturnsLineBreakTagName() + { + CreateHtmlReader("
"); + htmlReader.Read(); + + string value = htmlReader.Value; + + Assert.AreEqual("br", value); + } + + [Test] + public void Length_ReadSingleLineBreakTag_ReturnsFour() + { + CreateHtmlReader("
"); + htmlReader.Read(); + + int length = htmlReader.Length; + + Assert.AreEqual(4, length); + } + + [Test] + public void Value_ReadParagraphTagFollowedByBoldTag_ReturnsBoldTagName() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + string value = htmlReader.Value; + + Assert.AreEqual("b", value); + } + + [Test] + public void Offset_ReadParagraphTagFollowedByBoldTag_ReturnsThree() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + int offset = htmlReader.Offset; + + Assert.AreEqual(3, offset); + } + + [Test] + public void Value_ReadParagraphTagFollowedByEndParagraphTag_ReturnsParagraphTagName() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + string value = htmlReader.Value; + + Assert.AreEqual("p", value); + } + + [Test] + public void IsEndElement_ReadParagraphTagFollowedByEndParagraphTag_ReturnsTrue() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + bool result = htmlReader.IsEndElement; + + Assert.IsTrue(result); + } + + [Test] + public void IsEmptyElement_ReadEmptyBreakTagElement_ReturnsTrue() + { + CreateHtmlReader("
"); + htmlReader.Read(); + + bool result = htmlReader.IsEmptyElement; + + Assert.IsTrue(result); + } + + [Test] + public void IsEmptyElement_ReadParagraphTagFollowedByEndParagraphTag_ReturnsFalse() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + bool result = htmlReader.IsEmptyElement; + + Assert.IsFalse(result); + } + + [Test] + public void Length_ReadParagraphTagFollowedByEndParagraphTag_ReturnsFour() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + int length = htmlReader.Length; + + Assert.AreEqual(4, length); + } + + [Test] + public void IsStartElement_ReadSingleParagraphTag_ReturnTrue() + { + CreateHtmlReader("

"); + htmlReader.Read(); + + bool result = htmlReader.IsStartElement; + + Assert.IsTrue(result); + } + + [Test] + public void IsEmptyElement_ReadEmptyBreakTagFollowedbySingleParagraphTag_ReturnFalse() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + bool result = htmlReader.IsEmptyElement; + + Assert.IsFalse(result); + } + + [Test] + public void IsStartElement_ReadEmptyBreakTag_ReturnsTrue() + { + CreateHtmlReader("
"); + htmlReader.Read(); + + bool result = htmlReader.IsStartElement; + + Assert.IsTrue(result); + } + + [Test] + public void IsStartElement_ReadParagraphTagFollowedByEndParagraphTag_ReturnsFalse() + { + CreateHtmlReader("

"); + htmlReader.Read(); + htmlReader.Read(); + + bool result = htmlReader.IsStartElement; + + Assert.IsFalse(result); + } + + [Test] + public void Value_ReadParagraphTagsContainingText_ReturnsParagraphTagName() + { + CreateHtmlReader("

abcdef

"); + htmlReader.Read(); + htmlReader.Read(); + + string value = htmlReader.Value; + + Assert.AreEqual("p", value); + } + + [Test] + public void Value_ReadDivStartTagWithClassAttribute_ReturnsDivTagName() + { + CreateHtmlReader("
"); + htmlReader.Read(); + + string value = htmlReader.Value; + + Assert.AreEqual("div", value); + } + + [Test] + public void Value_ScriptStartTagWithForwardSlashInTypeAttribute_ReturnsScriptTagName() + { + CreateHtmlReader("