public void Parse_NoCodesShouldBeUnchanged( ShortcodeParser sut ) { const string content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus eu faucibus leo. Morbi ligula tellus, convallis non lorem vel, ultricies viverra diam. Pellentesque eget ligula eleifend, venenatis purus nec, tincidunt ante. Maecenas nec pellentesque lectus, vel bibendum sem. Sed eget vulputate lacus, mollis convallis massa. Quisque in ante ut turpis euismod elementum interdum in purus. Phasellus viverra nec est ut scelerisque."; var result = sut.Parse(null, content); result.ToString().Should().Be(content); }
public void ThrowsForExtraClosingContent() { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes("<?# foo ?>abc<?#/ foo bar ?>")); IShortcodeCollection shortcodeCollection = new TestShortcodeCollection(); shortcodeCollection.Add("foo", typeof(TestShortcode)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultStartDelimiter, ShortcodeParser.DefaultEndDelimiter, shortcodeCollection); // When, Then Should.Throw <ShortcodeParserException>(() => parser.Parse(stream)); }
public void ThrowsForUnregisteredShortcodeName() { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes("<?# foo ?>abc<?#/ foo ?>")); IShortcodeCollection shortcodeCollection = new TestShortcodeCollection(); shortcodeCollection.Add("bar", typeof(TestShortcode)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultPostRenderStartDelimiter, ShortcodeParser.DefaultPostRenderEndDelimiter, shortcodeCollection); // When, Then Should.Throw <ShortcodeParserException>(() => parser.Parse(stream)); }
public void Parse_MatchedContentIsReplaced( [Frozen] IRenderShortcode renderShortcode, ShortcodeParser sut, IHtmlHelper helper ) { A.CallTo(() => renderShortcode.CanRender("test")).Returns(true); A.CallTo(() => renderShortcode.Render(helper, "test", A <Dictionary <string, string> > .Ignored)) .Returns("Updated text"); const string content = "[test]"; var result = sut.Parse(helper, content); result.ToString().Should().Be("Updated text"); }
public void ThrowsForExtraClosingContent() { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes("<?# foo ?>abc<?#/ foo bar ?>")); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultPostRenderStartDelimiter, ShortcodeParser.DefaultPostRenderEndDelimiter, new TestShortcodeCollection { { "foo", (Type)null } }); // When, Then Should.Throw <ShortcodeParserException>(() => parser.Parse(stream)); }
public void ThrowsForUnterminatedShortcode(string input) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); IShortcodeCollection shortcodeCollection = new TestShortcodeCollection(); shortcodeCollection.Add("foo", typeof(TestShortcode)); shortcodeCollection.Add("bar", typeof(TestShortcode)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultStartDelimiter, ShortcodeParser.DefaultEndDelimiter, shortcodeCollection); // When, Then Should.Throw <ShortcodeParserException>(() => parser.Parse(stream)); }
public void ThrowsForUnterminatedShortcode(string input) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultPostRenderStartDelimiter, ShortcodeParser.DefaultPostRenderEndDelimiter, new TestShortcodeCollection { { "foo", (Type)null }, { "bar", (Type)null } }); // When, Then Should.Throw <ShortcodeParserException>(() => parser.Parse(stream)); }
public void TrimsWildcardProcessingInstructionFromContent(string input, string content) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); IShortcodeCollection shortcodeCollection = new TestShortcodeCollection(); shortcodeCollection.Add("name", typeof(TestShortcode)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultStartDelimiter, ShortcodeParser.DefaultEndDelimiter, shortcodeCollection); // When List <ShortcodeLocation> result = parser.Parse(stream); // Then result.Single().Content.ShouldBe(content); }
public void Parse_GetsAttributesFromShortcode( [Frozen] IRenderShortcode renderShortcode, ShortcodeParser sut, IHtmlHelper helper ) { A.CallTo(() => renderShortcode.CanRender("test")).Returns(true); A.CallTo(() => renderShortcode.Render(helper, "test", A <Dictionary <string, string> > .That.Matches( dictionary => dictionary.ContainsKey("id") && dictionary["id"] == "123"))) .Returns("Updated text"); const string content = "[test id=\"123\"]"; var result = sut.Parse(helper, content); result.ToString().Should().Be("Updated text"); }
public void TrimsWildcardProcessingInstructionFromContent(string input, string content) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultPostRenderStartDelimiter, ShortcodeParser.DefaultPostRenderEndDelimiter, new TestShortcodeCollection { { "name", (Type)null } }); // When List <ShortcodeLocation> result = parser.Parse(stream); // Then result.Single().Content.ShouldBe(content); }
public void FindsClosingShortcode(string input, int firstIndex, int lastIndex) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); IShortcodeCollection shortcodeCollection = new TestShortcodeCollection(); shortcodeCollection.Add("name", typeof(TestShortcode)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultStartDelimiter, ShortcodeParser.DefaultEndDelimiter, shortcodeCollection); // When List <ShortcodeLocation> result = parser.Parse(stream); // Then result.Single().FirstIndex.ShouldBe(firstIndex); result.Single().LastIndex.ShouldBe(lastIndex); }
public void FindsClosingShortcode(string input, int firstIndex, int lastIndex) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); ShortcodeParser parser = new ShortcodeParser( ShortcodeParser.DefaultPostRenderStartDelimiter, ShortcodeParser.DefaultPostRenderEndDelimiter, new TestShortcodeCollection { { "name", (Type)null } }); // When List <ShortcodeLocation> result = parser.Parse(stream); // Then result.Single().FirstIndex.ShouldBe(firstIndex); result.Single().LastIndex.ShouldBe(lastIndex); }
public void Parse_WithQuotes( [Frozen] IRenderShortcode renderShortcode, ShortcodeParser sut, IHtmlHelper helper ) { var content = "[test id=\"123\"] sdfs adfsdf sdaf sadf asdfsdaf sdf sdf more=(*&)(&knkjdhnf data]"; A.CallTo(() => renderShortcode.CanRender("test")).Returns(true); A.CallTo(() => renderShortcode.Render(helper, "test ", A <Dictionary <string, string> > .That.Matches( dictionary => dictionary.ContainsKey("id") && dictionary["id"] == "123" && !dictionary.ContainsKey("more") ))); var result = sut.Parse(helper, content); result.ToString().Should().Be(" sdfs adfsdf sdaf sadf asdfsdaf sdf sdf more=(*&)(&knkjdhnf data]"); }
public void SupportsAlternateDelimiters( string input, int firstIndex, int lastIndex, string startDelimiter, string endDelimiter) { // Given Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); ShortcodeParser parser = new ShortcodeParser( startDelimiter, endDelimiter, new TestShortcodeCollection { { "name", (Type)null } }); // When List <ShortcodeLocation> result = parser.Parse(stream); // Then result.Single().FirstIndex.ShouldBe(firstIndex); result.Single().LastIndex.ShouldBe(lastIndex); }
// The inputStream will be disposed if this returns <c>true</c> but will not otherwise private bool ProcessShortcodes(Stream inputStream, IDocument input, IExecutionContext context, out IDocument result) { // Parse the input stream looking for shortcodes ShortcodeParser parser = new ShortcodeParser(_startDelimiter, _endDelimiter, context.Shortcodes); if (!inputStream.CanSeek) { inputStream = new SeekableStream(inputStream, true); } List <ShortcodeLocation> locations = parser.Parse(inputStream); // Reset the position because we're going to use the stream again when we do replacements inputStream.Position = 0; // Return the original document if we didn't find any if (locations.Count == 0) { result = null; return(false); } // Otherwise, create a shortcode instance for each named shortcode Dictionary <string, IShortcode> shortcodes = locations .Select(x => x.Name) .Distinct(StringComparer.OrdinalIgnoreCase) .ToDictionary(x => x, x => context.Shortcodes.CreateInstance(x), StringComparer.OrdinalIgnoreCase); // Execute each of the shortcodes in order List <InsertingStreamLocation> insertingLocations = locations .Select(x => { // Execute the shortcode IShortcodeResult shortcodeResult = shortcodes[x.Name].Execute(x.Arguments, x.Content, input, context); // Merge output metadata with the current input document // Creating a new document is the easiest way to ensure all the metadata from shortcodes gets accumulated correctly if (shortcodeResult?.Metadata != null) { input = context.GetDocument(input, shortcodeResult.Metadata); } // Recursively parse shortcodes Stream shortcodeResultStream = shortcodeResult?.Stream; if (shortcodeResultStream != null) { // Don't process nested shortcodes if it's the raw shortcode if (!x.Name.Equals(nameof(Core.Shortcodes.Contents.Raw), StringComparison.OrdinalIgnoreCase)) { if (!shortcodeResultStream.CanSeek) { shortcodeResultStream = new SeekableStream(shortcodeResultStream, true); } if (ProcessShortcodes(shortcodeResultStream, input, context, out IDocument nestedResult)) { input = nestedResult; shortcodeResultStream = nestedResult.GetStream(); // Will get disposed in the replacement operation below } else { shortcodeResultStream.Position = 0; } } return(new InsertingStreamLocation(x.FirstIndex, x.LastIndex, shortcodeResultStream)); } return(new InsertingStreamLocation(x.FirstIndex, x.LastIndex, null)); }) .ToList(); // Dispose any shortcodes that implement IDisposable foreach (IDisposable disposableShortcode in shortcodes.Values.Select(x => x as IDisposable).Where(x => x != null)) { disposableShortcode.Dispose(); } // Construct a new stream with the shortcode results inserted // We have to use all TextWriter/TextReaders over the streams to ensure consistent encoding Stream resultStream = context.GetContentStream(); char[] buffer = new char[4096]; using (TextWriter writer = new StreamWriter(resultStream, Encoding.UTF8, 4096, true)) { // The input stream will get disposed when the reader is using (TextReader reader = new StreamReader(inputStream)) { int position = 0; int length = 0; foreach (InsertingStreamLocation insertingLocation in insertingLocations) { // Copy up to the start of this shortcode length = insertingLocation.FirstIndex - position; Read(reader, writer, length, ref buffer); position += length; // Copy the shortcode content to the result stream if (insertingLocation.Stream != null) { // This will dispose the shortcode content stream when done using (TextReader insertingReader = new StreamReader(insertingLocation.Stream)) { Read(insertingReader, writer, null, ref buffer); } } // Skip the shortcode text length = insertingLocation.LastIndex - insertingLocation.FirstIndex + 1; Read(reader, null, length, ref buffer); position += length; } // Copy remaining Read(reader, writer, null, ref buffer); } } result = context.GetDocument(input, resultStream); return(true); }
public void Parse_NullContentReturnsEmptyString( ShortcodeParser sut ) { sut.Parse(null, null).Should().Be(MvcHtmlString.Empty); }