2021-05-23 17:30:41 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2022-06-17 15:37:17 +08:00
#nullable disable
2021-05-25 10:56:26 +08:00
using System ;
2021-06-29 11:14:51 +08:00
using System.Linq ;
2021-05-25 10:56:26 +08:00
using Markdig.Syntax.Inlines ;
2021-05-23 17:30:41 +08:00
using NUnit.Framework ;
using osu.Framework.Allocation ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
2021-06-29 11:14:51 +08:00
using osu.Framework.Graphics.Sprites ;
using osu.Framework.Testing ;
using osu.Framework.Utils ;
2021-05-25 10:56:26 +08:00
using osu.Game.Graphics.Containers.Markdown ;
2022-12-17 04:53:31 +08:00
using osu.Game.Graphics.Containers.Markdown.Footnotes ;
2021-05-23 17:30:41 +08:00
using osu.Game.Overlays ;
using osu.Game.Overlays.Wiki.Markdown ;
2022-12-20 04:31:54 +08:00
using osu.Game.Users.Drawables ;
2022-12-17 04:53:31 +08:00
using osuTK.Input ;
2021-05-23 17:30:41 +08:00
namespace osu.Game.Tests.Visual.Online
{
2022-12-17 04:53:31 +08:00
public partial class TestSceneWikiMarkdownContainer : OsuManualInputManagerTestScene
2021-05-23 17:30:41 +08:00
{
2022-12-17 04:53:31 +08:00
private OverlayScrollContainer scrollContainer ;
2021-05-25 10:56:26 +08:00
private TestMarkdownContainer markdownContainer ;
2021-05-23 17:30:41 +08:00
[Cached]
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider ( OverlayColourScheme . Orange ) ;
[SetUp]
public void Setup ( ) = > Schedule ( ( ) = >
{
Children = new Drawable [ ]
{
new Box
{
Colour = overlayColour . Background5 ,
RelativeSizeAxes = Axes . Both ,
} ,
2022-12-17 04:34:12 +08:00
scrollContainer = new OverlayScrollContainer
2021-05-23 17:30:41 +08:00
{
RelativeSizeAxes = Axes . Both ,
Padding = new MarginPadding ( 20 ) ,
2022-12-17 04:34:12 +08:00
}
} ;
scrollContainer . Child = new DependencyProvidingContainer
{
CachedDependencies = new ( Type , object ) [ ]
{
( typeof ( OverlayScrollContainer ) , scrollContainer )
} ,
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Child = markdownContainer = new TestMarkdownContainer
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2021-05-23 17:30:41 +08:00
}
} ;
} ) ;
[Test]
public void TestLink ( )
{
2021-06-01 13:38:30 +08:00
AddStep ( "set current path" , ( ) = > markdownContainer . CurrentPath = $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/" ) ;
2021-05-23 17:30:41 +08:00
AddStep ( "set '/wiki/Main_Page''" , ( ) = > markdownContainer . Text = "[wiki main page](/wiki/Main_Page)" ) ;
2021-05-26 23:12:22 +08:00
AddAssert ( "check url" , ( ) = > markdownContainer . Link . Url = = $"{API.WebsiteRootUrl}/wiki/Main_Page" ) ;
2021-05-23 17:30:41 +08:00
AddStep ( "set '../FAQ''" , ( ) = > markdownContainer . Text = "[FAQ](../FAQ)" ) ;
2021-05-26 23:12:22 +08:00
AddAssert ( "check url" , ( ) = > markdownContainer . Link . Url = = $"{API.WebsiteRootUrl}/wiki/FAQ" ) ;
2021-05-23 17:30:41 +08:00
AddStep ( "set './Writing''" , ( ) = > markdownContainer . Text = "[wiki writing guidline](./Writing)" ) ;
2021-05-26 23:12:22 +08:00
AddAssert ( "check url" , ( ) = > markdownContainer . Link . Url = = $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/Writing" ) ;
2021-05-23 17:30:41 +08:00
AddStep ( "set 'Formatting''" , ( ) = > markdownContainer . Text = "[wiki formatting guidline](Formatting)" ) ;
2021-05-26 23:12:22 +08:00
AddAssert ( "check url" , ( ) = > markdownContainer . Link . Url = = $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/Formatting" ) ;
2021-05-23 17:30:41 +08:00
}
2021-05-25 10:56:26 +08:00
2021-05-23 18:29:48 +08:00
[Test]
public void TestOutdatedNoticeBox ( )
{
AddStep ( "Add outdated yaml header" , ( ) = >
{
markdownContainer . Text = @ "---
outdated : true
- - - ";
} ) ;
}
[Test]
public void TestNeedsCleanupNoticeBox ( )
{
AddStep ( "Add needs cleanup yaml header" , ( ) = >
{
markdownContainer . Text = @ "---
needs_cleanup : true
- - - ";
} ) ;
}
[Test]
public void TestOnlyShowOutdatedNoticeBox ( )
{
AddStep ( "Add outdated and needs cleanup yaml" , ( ) = >
{
markdownContainer . Text = @ "---
outdated : true
needs_cleanup : true
- - - ";
} ) ;
}
2021-05-23 17:30:41 +08:00
[Test]
public void TestAbsoluteImage ( )
{
AddStep ( "Add absolute image" , ( ) = >
{
2023-01-25 06:01:53 +08:00
markdownContainer . CurrentPath = "https://dev.ppy.sh" ;
2023-01-25 06:58:39 +08:00
markdownContainer . Text = "![intro](/wiki/images/Client/Interface/img/intro-screen.jpg)" ;
2021-05-23 17:30:41 +08:00
} ) ;
}
[Test]
public void TestRelativeImage ( )
{
AddStep ( "Add relative image" , ( ) = >
{
2023-01-25 06:01:53 +08:00
markdownContainer . CurrentPath = "https://dev.ppy.sh/wiki/Interface/" ;
markdownContainer . Text = "![intro](../images/Client/Interface/img/intro-screen.jpg)" ;
2021-05-23 17:30:41 +08:00
} ) ;
}
2021-05-23 18:22:31 +08:00
[Test]
public void TestBlockImage ( )
{
AddStep ( "Add paragraph with block image" , ( ) = >
{
2023-01-25 06:01:53 +08:00
markdownContainer . CurrentPath = "https://dev.ppy.sh/wiki/Interface/" ;
2021-05-23 18:22:31 +08:00
markdownContainer . Text = @ "Line before image
2023-01-25 06:01:53 +08:00
! [ play menu ] ( . . / images / Client / Interface / img / play - menu . jpg "" Main Menu in osu ! "" )
2021-05-23 18:22:31 +08:00
Line after image ";
} ) ;
}
[Test]
public void TestInlineImage ( )
{
AddStep ( "Add inline image" , ( ) = >
{
2023-01-25 06:01:53 +08:00
markdownContainer . CurrentPath = "https://dev.ppy.sh" ;
2023-01-25 07:00:16 +08:00
markdownContainer . Text = "![osu! mode icon](/wiki/shared/mode/osu.png) osu!" ;
2021-05-23 18:22:31 +08:00
} ) ;
}
2021-06-10 10:37:33 +08:00
[Test]
public void TestTableWithImageContent ( )
{
AddStep ( "Add Table" , ( ) = >
{
2023-01-25 06:01:53 +08:00
markdownContainer . CurrentPath = "https://dev.ppy.sh" ;
2021-06-10 10:37:33 +08:00
markdownContainer . Text = @ "
| Image | Name | Effect |
| : - : | : - : | : - - |
2023-01-25 07:00:16 +08:00
| ! [ ] ( / wiki / images / shared / judgement / osu ! / hit300 . png "" 300 "" ) | 300 | A possible score when tapping a hit circle precisely on time , completing a Slider and keeping the cursor over every tick , or completing a Spinner with the Spinner Metre full . A score of 300 appears in an blue score by default . Scoring nothing except 300 s in a beatmap will award the player with the SS or SSH grade . |
| ! [ ] ( / wiki / images / shared / judgement / osu ! / hit300g . png "" Geki "" ) | ( 激 ) Geki | A term from Ouendan , called Elite Beat ! in EBA . Appears when playing the last element in a combo in which the player has scored only 300 s . Getting a Geki will give a sizable boost to the Life Bar . By default , it is blue . |
| ! [ ] ( / wiki / images / shared / judgement / osu ! / hit100 . png "" 100 "" ) | 100 | A possible score one can get when tapping a Hit Object slightly late or early , completing a Slider and missing a number of ticks , or completing a Spinner with the Spinner Meter almost full . A score of 100 appears in a green score by default . When very skilled players test a beatmap and they get a lot of 100 s , this may mean that the beatmap does not have correct timing . |
| ! [ ] ( / wiki / images / shared / judgement / osu ! / hit300k . png "" 300 Katu "" ) ! [ ] ( / wiki / Skinning / Interface / img / hit100k . png "" 100 Katu "" ) | ( 喝 ) Katu or Katsu | A term from Ouendan , called Beat ! in EBA . Appears when playing the last element in a combo in which the player has scored at least one 100 , but no 50 s or misses . Getting a Katu will give a small boost to the Life Bar . By default , it is coloured green or blue depending on whether the Katu itself is a 100 or a 300. |
| ! [ ] ( / wiki / images / shared / judgement / osu ! / hit50 . png "" 50 "" ) | 50 | A possible score one can get when tapping a hit circle rather early or late but not early or late enough to cause a miss , completing a Slider and missing a lot of ticks , or completing a Spinner with the Spinner Metre close to full . A score of 50 appears in a orange score by default . Scoring a 50 in a combo will prevent the appearance of a Katu or a Geki at the combo ' s end . |
| ! [ ] ( / wiki / images / shared / judgement / osu ! / hit0 . png "" Miss "" ) | Miss | A possible score one can get when not tapping a hit circle or too early ( based on OD and AR , it may * shake * instead ) , not tapping or holding the Slider at least once , or completing a Spinner with low Spinner Metre fill . Scoring a Miss will reset the current combo to 0 and will prevent the appearance of a Katu or a Geki at the combo ' s end . |
2021-06-10 10:37:33 +08:00
";
} ) ;
}
2021-06-28 21:50:29 +08:00
[Test]
public void TestWideImageNotExceedContainer ( )
{
AddStep ( "Add image" , ( ) = >
{
2023-01-25 06:01:53 +08:00
markdownContainer . CurrentPath = "https://dev.ppy.sh/wiki/osu!_Program_Files/" ;
markdownContainer . Text = "![](../images/Client/Program_files/img/file_structure.jpg \"The file structure of osu!'s installation folder, on Windows and macOS\")" ;
2021-06-28 21:50:29 +08:00
} ) ;
2021-06-29 11:14:51 +08:00
AddUntilStep ( "Wait image to load" , ( ) = > markdownContainer . ChildrenOfType < DelayedLoadWrapper > ( ) . First ( ) . DelayedLoadCompleted ) ;
2021-06-28 21:50:29 +08:00
AddStep ( "Change container width" , ( ) = >
{
markdownContainer . Width = 0.5f ;
} ) ;
2021-06-29 11:14:51 +08:00
AddAssert ( "Image not exceed container width" , ( ) = >
{
var spriteImage = markdownContainer . ChildrenOfType < Sprite > ( ) . First ( ) ;
return Precision . DefinitelyBigger ( markdownContainer . DrawWidth , spriteImage . DrawWidth ) ;
} ) ;
2021-06-28 21:50:29 +08:00
}
2022-10-09 02:50:31 +08:00
[Test]
public void TestFlag ( )
{
AddStep ( "Add flag" , ( ) = >
{
markdownContainer . CurrentPath = @"https://dev.ppy.sh" ;
2022-10-11 00:38:37 +08:00
markdownContainer . Text = "::{flag=\"AU\"}:: ::{flag=\"ZZ\"}::" ;
2022-10-09 02:50:31 +08:00
} ) ;
2022-12-20 04:31:54 +08:00
AddAssert ( "Two flags visible" , ( ) = > markdownContainer . ChildrenOfType < DrawableFlag > ( ) . Count ( ) , ( ) = > Is . EqualTo ( 2 ) ) ;
2022-10-09 02:50:31 +08:00
}
2022-12-20 04:21:18 +08:00
[Test]
public void TestHeadingWithIdAttribute ( )
{
AddStep ( "Add heading with ID" , ( ) = >
{
markdownContainer . Text = "# This is a heading with an ID {#this-is-the-id}" ;
} ) ;
AddAssert ( "ID not visible" , ( ) = > markdownContainer . ChildrenOfType < SpriteText > ( ) . All ( spriteText = > spriteText . Text ! = "{#this-is-the-id}" ) ) ;
}
2022-12-17 03:57:27 +08:00
[Test]
public void TestFootnotes ( )
{
AddStep ( "set content" , ( ) = > markdownContainer . Text = @ "This text has a footnote[^test].
Here ' s some more text [ ^ test2 ] with another footnote !
2022-12-17 04:34:12 +08:00
# Lorem ipsum dolor sit amet , consectetur adipiscing elit . Nullam efficitur laoreet posuere . Ut accumsan tortor in ipsum tincidunt ultrices . Suspendisse a malesuada tellus . Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas . Fusce a sagittis nibh . In et velit sit amet mauris aliquet consectetur quis vehicula lorem . Etiam sit amet tellus ac velit ornare maximus . Donec quis metus eget libero ullamcorper imperdiet id vitae arcu . Vivamus iaculis rhoncus purus malesuada mollis . Vestibulum dictum at nisi sed tincidunt . Suspendisse finibus , ipsum ut dapibus commodo , leo eros porttitor sapien , non scelerisque nisi ligula sed ex . Pellentesque magna orci , hendrerit eu iaculis sit amet , ullamcorper in urna . Vivamus dictum mauris orci , nec facilisis dolor fringilla eu . Sed at porttitor nisi , at venenatis urna . Ut at orci vitae libero semper ullamcorper eu ut risus . Mauris hendrerit varius enim , ut varius nisi feugiat mattis .
# # In at eros urna . Sed ipsum lorem , tempor sit amet purus in , vehicula pellentesque leo . Fusce volutpat pellentesque velit sit amet porttitor . Nulla eget erat ex . Praesent eu lacinia est , quis vehicula lacus . Donec consequat ultrices neque , at finibus quam efficitur vel . Vestibulum molestie nisl sit amet metus semper , at vestibulum massa rhoncus . Quisque imperdiet suscipit augue , et dignissim odio eleifend ut .
Aliquam sed vestibulum mauris , ut lobortis elit . Sed quis lacinia erat . Nam ultricies , risus non pellentesque sollicitudin , mauris dolor tincidunt neque , ac porta ipsum dui quis libero . Integer eget velit neque . Vestibulum venenatis mauris vitae rutrum vestibulum . Maecenas suscipit eu purus eu tempus . Nam dui nisl , bibendum condimentum mollis et , gravida vel dui . Sed et eros rutrum , facilisis sapien eu , mattis ligula . Fusce finibus pulvinar dolor quis consequat .
Donec ipsum felis , feugiat vel fermentum at , commodo eu sapien . Suspendisse nec enim vitae felis laoreet laoreet . Phasellus purus quam , fermentum a pharetra vel , tempor et urna . Integer vitae quam diam . Aliquam tincidunt tortor a iaculis convallis . Suspendisse potenti . Cras quis risus quam . Nullam tincidunt in lorem posuere sagittis .
Phasellus eu nunc nec ligula semper fringilla . Aliquam magna neque , placerat sed urna tristique , laoreet pharetra nulla . Vivamus maximus turpis purus , eu viverra dolor sodales porttitor . Praesent bibendum sapien purus , sed ultricies dolor iaculis sed . Fusce congue hendrerit malesuada . Nulla nulla est , auctor ac fringilla sed , ornare a lorem . Donec quis velit imperdiet , imperdiet sem non , pellentesque sapien . Maecenas in orci id ipsum placerat facilisis non sed nisi . Duis dictum lorem sodales odio dictum eleifend . Vestibulum bibendum euismod quam , eget pharetra orci facilisis sed . Vivamus at diam non ipsum consequat tristique . Pellentesque gravida dignissim pellentesque . Donec ullamcorper lacinia orci , id consequat purus faucibus quis . Phasellus metus nunc , iaculis a interdum vel , congue sed erat . Orci varius natoque penatibus et magnis dis parturient montes , nascetur ridiculus mus . Etiam eros libero , hendrerit luctus nulla vitae , luctus maximus nunc .
2022-12-17 03:57:27 +08:00
[^test] : This is a * * footnote * * .
[^test2] : This is another footnote [ with a link ] ( https : //google.com/)!");
2022-12-17 04:53:31 +08:00
AddStep ( "shrink scroll height" , ( ) = > scrollContainer . Height = 0.5f ) ;
AddStep ( "press second footnote link" , ( ) = >
{
InputManager . MoveMouseTo ( markdownContainer . ChildrenOfType < OsuMarkdownFootnoteLink > ( ) . ElementAt ( 1 ) ) ;
InputManager . Click ( MouseButton . Left ) ;
} ) ;
AddUntilStep ( "second footnote scrolled into view" , ( ) = >
{
var footnote = markdownContainer . ChildrenOfType < OsuMarkdownFootnote > ( ) . ElementAt ( 1 ) ;
return scrollContainer . ScreenSpaceDrawQuad . Contains ( footnote . ScreenSpaceDrawQuad . TopLeft )
& & scrollContainer . ScreenSpaceDrawQuad . Contains ( footnote . ScreenSpaceDrawQuad . BottomRight ) ;
} ) ;
AddStep ( "press first footnote backlink" , ( ) = >
{
InputManager . MoveMouseTo ( markdownContainer . ChildrenOfType < OsuMarkdownFootnoteBacklink > ( ) . First ( ) ) ;
InputManager . Click ( MouseButton . Left ) ;
} ) ;
AddUntilStep ( "first footnote link scrolled into view" , ( ) = >
{
var footnote = markdownContainer . ChildrenOfType < OsuMarkdownFootnoteLink > ( ) . First ( ) ;
return scrollContainer . ScreenSpaceDrawQuad . Contains ( footnote . ScreenSpaceDrawQuad . TopLeft )
& & scrollContainer . ScreenSpaceDrawQuad . Contains ( footnote . ScreenSpaceDrawQuad . BottomRight ) ;
} ) ;
2022-12-17 03:57:27 +08:00
}
2023-01-24 08:51:40 +08:00
[Test]
public void TestCodeSyntax ( )
{
AddStep ( "set content" , ( ) = >
{
markdownContainer . Text = @ "
This is a paragraph containing ` inline code ` synatax .
Oh wow I do love the ` WikiMarkdownContainer ` , it is very cool !
This is a line before the fenced code block :
` ` ` csharp
public class WikiMarkdownContainer : MarkdownContainer
{
public WikiMarkdownContainer ( )
{
this . foo = bar ;
}
}
` ` `
This is a line after the fenced code block !
";
} ) ;
}
2021-05-25 10:56:26 +08:00
private partial class TestMarkdownContainer : WikiMarkdownContainer
{
public LinkInline Link ;
2023-11-16 11:43:25 +08:00
public override OsuMarkdownTextFlowContainer CreateTextFlow ( ) = > new TestMarkdownTextFlowContainer
2021-05-25 10:56:26 +08:00
{
UrlAdded = link = > Link = link ,
} ;
private partial class TestMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
{
public Action < LinkInline > UrlAdded ;
protected override void AddLinkText ( string text , LinkInline linkInline )
{
base . AddLinkText ( text , linkInline ) ;
UrlAdded ? . Invoke ( linkInline ) ;
}
2021-05-26 16:04:04 +08:00
protected override void AddImage ( LinkInline linkInline ) = > AddDrawable ( new WikiMarkdownImage ( linkInline ) ) ;
2021-05-25 10:56:26 +08:00
}
}
2021-05-23 17:30:41 +08:00
}
}