A game framework written with osu! in mind.
at master 352 lines 12 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Collections.Generic; 6using System.Threading.Tasks; 7using Markdig.Syntax.Inlines; 8using NUnit.Framework; 9using osu.Framework.Graphics; 10using osu.Framework.Graphics.Containers; 11using osu.Framework.Graphics.Containers.Markdown; 12using osu.Framework.Graphics.Sprites; 13using osu.Framework.IO.Network; 14 15namespace osu.Framework.Tests.Visual.UserInterface 16{ 17 public class TestSceneMarkdownContainer : FrameworkTestScene 18 { 19 private TestMarkdownContainer markdownContainer; 20 21 [SetUp] 22 public void Setup() => Schedule(() => 23 { 24 Child = new BasicScrollContainer 25 { 26 RelativeSizeAxes = Axes.Both, 27 Child = markdownContainer = new TestMarkdownContainer 28 { 29 RelativeSizeAxes = Axes.X, 30 AutoSizeAxes = Axes.Y 31 } 32 }; 33 }); 34 35 [Test] 36 public void TestHeading() 37 { 38 AddStep("Markdown Heading", () => 39 { 40 markdownContainer.Text = @"# Header 1 41## Header 2 42### Header 3 43#### Header 4 44##### Header 5"; 45 }); 46 } 47 48 [Test] 49 public void TestSeparator() 50 { 51 AddStep("Markdown Seperator", () => 52 { 53 markdownContainer.Text = @"Line above 54 55--- 56 57Line below"; 58 }); 59 } 60 61 [Test] 62 public void TestUnorderedList() 63 { 64 AddStep("Markdown Unordered List", () => 65 { 66 markdownContainer.Text = @"- [1. Blocks](#1-blocks) 67 - [1.1 Code block](#11-code-block) 68 - [1.2 Text block](#12-text-block) 69 - [1.3 Escape block](#13-escape-block) 70 - [1.4 Whitespace control](#14-whitespace-control) 71- [2 Comments](#2-comments) 72- [3 Literals](#3-literals) 73 - [3.1 Strings](#31-strings) 74 - [3.2 Numbers](#32-numbers) 75 - [3.3 Boolean](#33-boolean) 76 - [3.4 null](#34-null)"; 77 }); 78 } 79 80 [Test] 81 public void TestQuote() 82 { 83 AddStep("Markdown Quote", () => { markdownContainer.Text = @"> **input**"; }); 84 } 85 86 [Test] 87 public void TestFencedCode() 88 { 89 AddStep("Markdown Fenced Code", () => 90 { 91 markdownContainer.Text = @"```scriban-html 92 93[Escape me] 94[[Escape me]] 95 96{{ 97 x = ""5"" # This assignment will not output anything 98 x # This expression will print 5 99 x + 1 # This expression will print 6 100}} 101```"; 102 }); 103 } 104 105 [Test] 106 public void TestTable() 107 { 108 AddStep("Markdown Table", () => 109 { 110 markdownContainer.Text = 111 @"|Operator | Description 112|--------------------|------------ 113| `'left' + <right>` | concatenates left to right string: `""ab"" + ""c"" -> ""abc""` 114| `'left' * <right>` | concatenates the left string `right` times: `'a' * 5 -> aaaaa`. left and right and be swapped as long as there is one string and one number."; 115 }); 116 } 117 118 [Test] 119 public void TestTableAlignment() 120 { 121 AddStep("Markdown Table (Aligned)", () => 122 { 123 markdownContainer.Text = 124 @"| Left-Aligned | Center Aligned | Right Aligned | 125| :------------ |:---------------:| -----:| 126| col 3 is | some wordy text | $1600 | 127| col 2 is | centered | $12 | 128| zebra stripes | are neat | $1 |"; 129 }); 130 } 131 132 [Test] 133 public void TestParagraph() 134 { 135 AddStep("Markdown Paragraph", () => 136 { 137 markdownContainer.Text = @"A text enclosed by `{{` and `}}` is a scriban **code block** that will be evaluated by the scriban templating engine. 138 139The greedy mode using the character - (e.g {{- or -}}), removes any whitespace, including newlines Examples with the variable name = ""foo"":"; 140 }); 141 } 142 143 [Test] 144 public void TestLink() 145 { 146 AddStep("MarkdownLink", () => { markdownContainer.Text = @"[click the circles to the beat](https://osu.ppy.sh)"; }); 147 } 148 149 [Test] 150 public void TestImage() 151 { 152 AddStep("MarkdownImage", () => { markdownContainer.Text = @"![peppy!](https://a.ppy.sh/2)"; }); 153 } 154 155 [Test] 156 public void TestMarkdownFromInternet() 157 { 158 WebRequest req = null; 159 160 AddStep("MarkdownFromInternet", () => 161 { 162 req = new WebRequest("https://raw.githubusercontent.com/ppy/osu-wiki/master/wiki/Skinning/skin.ini/en.md"); 163 req.Finished += () => Schedule(() => markdownContainer.Text = req.GetResponseString()); 164 165 Task.Run(() => req.PerformAsync()); 166 }); 167 168 AddUntilStep("wait for request completed", () => req.Completed); 169 170 AddAssert("ensure content", () => !string.IsNullOrEmpty(markdownContainer.Text)); 171 } 172 173 [Test] 174 public void TestEmphases() 175 { 176 AddStep("Emphases", () => 177 { 178 markdownContainer.Text = @"_italic with underscore_ 179*italic with asterisk* 180__bold with underscore__ 181**bold with asterisk** 182*__italic with asterisk, bold with underscore__* 183_**italic with underscore, bold with asterisk**_"; 184 }); 185 } 186 187 [Test] 188 public void TestLineBreaks() 189 { 190 AddStep("new lines", () => 191 { 192 markdownContainer.Text = @"line 1 193soft break\ 194soft break with '\'"; 195 }); 196 } 197 198 [Test] 199 public void TestRootRelativeLink() 200 { 201 AddStep("set content", () => 202 { 203 markdownContainer.DocumentUrl = "https://some.test.url/some/path/2"; 204 markdownContainer.Text = "[link](/file)"; 205 }); 206 207 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "https://some.test.url/file"); 208 } 209 210 [Test] 211 public void TestDocumentRelativeLink() 212 { 213 AddStep("set content", () => markdownContainer.DocumentUrl = "https://some.test.url/some/path/2"); 214 215 AddStep("set 'file'", () => markdownContainer.Text = "[link](file)"); 216 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "https://some.test.url/some/path/file"); 217 218 AddStep("set './file'", () => markdownContainer.Text = "[link](./file)"); 219 AddAssert("has correct link", () => markdownContainer.Links[1].Url == "https://some.test.url/some/path/file"); 220 221 AddStep("set '../folder/file'", () => markdownContainer.Text = "[link](../folder/file)"); 222 AddAssert("has correct link", () => markdownContainer.Links[2].Url == "https://some.test.url/some/folder/file"); 223 } 224 225 [Test] 226 public void TestDocumentRelativeLinkWithNoUri() 227 { 228 AddStep("set content", () => { markdownContainer.Text = "[link](file)"; }); 229 230 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "file"); 231 } 232 233 [Test] 234 public void TestRootRelativeLinkWithNoUri() 235 { 236 AddStep("set content", () => { markdownContainer.Text = "[link](/file)"; }); 237 238 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "/file"); 239 } 240 241 [Test] 242 public void TestDocumentRelativeLinkWithRootOverride() 243 { 244 AddStep("set content", () => 245 { 246 markdownContainer.DocumentUrl = "https://some.test.url/some/path/2"; 247 markdownContainer.RootUrl = "https://some.test.url/some/"; 248 markdownContainer.Text = "[link](file)"; 249 }); 250 251 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "https://some.test.url/some/path/file"); 252 } 253 254 [Test] 255 public void TestRootRelativeLinkWithRootOverride() 256 { 257 AddStep("set content", () => 258 { 259 markdownContainer.DocumentUrl = "https://some.test.url/some/path/2"; 260 markdownContainer.RootUrl = "https://some.test.url/some/"; 261 markdownContainer.Text = "[link](/file)"; 262 }); 263 264 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "https://some.test.url/some/file"); 265 } 266 267 [Test] 268 public void TestRootRelativeLinkWithRootOverrideCantEscape() 269 { 270 AddStep("set content", () => 271 { 272 markdownContainer.DocumentUrl = "https://some.test.url/some/path/2"; 273 markdownContainer.RootUrl = "https://some.test.url/some/"; 274 markdownContainer.Text = "[link](/../../../file)"; 275 }); 276 277 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "https://some.test.url/file"); 278 } 279 280 [Test] 281 public void TestAbsoluteLinkWithDifferentScheme() 282 { 283 AddStep("set content", () => 284 { 285 markdownContainer.DocumentUrl = "https://some.test.url/some/path/2"; 286 markdownContainer.RootUrl = "https://some.test.url/some/"; 287 markdownContainer.Text = "[link](mailto:contact@ppy.sh)"; 288 }); 289 290 AddAssert("has correct link", () => markdownContainer.Links[0].Url == "mailto:contact@ppy.sh"); 291 } 292 293 [Test] 294 public void TestAutoLinkInline() 295 { 296 AddStep("set content", () => 297 { 298 markdownContainer.Text = "<https://discord.gg/ppy>"; 299 }); 300 301 AddAssert("has correct autolink", () => markdownContainer.AutoLinks[0].Url == "https://discord.gg/ppy"); 302 } 303 304 private class TestMarkdownContainer : MarkdownContainer 305 { 306 public new string DocumentUrl 307 { 308 get => base.DocumentUrl; 309 set => base.DocumentUrl = value; 310 } 311 312 public new string RootUrl 313 { 314 get => base.RootUrl; 315 set => base.RootUrl = value; 316 } 317 318 public readonly List<LinkInline> Links = new List<LinkInline>(); 319 320 public readonly List<AutolinkInline> AutoLinks = new List<AutolinkInline>(); 321 322 public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer 323 { 324 UrlAdded = url => Links.Add(url), 325 AutoLinkAdded = autolink => AutoLinks.Add(autolink), 326 }; 327 328 public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With("Roboto", weight: "Regular")); 329 330 private class TestMarkdownTextFlowContainer : MarkdownTextFlowContainer 331 { 332 public Action<LinkInline> UrlAdded; 333 334 public Action<AutolinkInline> AutoLinkAdded; 335 336 protected override void AddLinkText(string text, LinkInline linkInline) 337 { 338 base.AddLinkText(text, linkInline); 339 340 UrlAdded?.Invoke(linkInline); 341 } 342 343 protected override void AddAutoLink(AutolinkInline autolinkInline) 344 { 345 base.AddAutoLink(autolinkInline); 346 347 AutoLinkAdded?.Invoke(autolinkInline); 348 } 349 } 350 } 351 } 352}