2019-01-24 16:43:03 +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.
2018-12-20 15:50:38 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2018-12-20 17:21:37 +08:00
using osu.Framework.Allocation ;
2018-12-20 15:50:38 +08:00
using osu.Framework.Graphics ;
using osu.Game.Online.Chat ;
using osuTK ;
2019-10-22 06:37:30 +08:00
using System ;
2019-10-29 13:32:38 +08:00
using System.Linq ;
2021-02-01 04:44:27 +08:00
using NUnit.Framework ;
2019-10-29 13:32:38 +08:00
using osu.Framework.Graphics.Containers ;
2022-03-05 04:27:09 +08:00
using osu.Framework.Testing ;
using osu.Framework.Utils ;
2022-06-08 17:54:23 +08:00
using osu.Game.Online.API ;
2021-11-04 17:02:44 +08:00
using osu.Game.Online.API.Requests.Responses ;
2019-10-29 13:32:38 +08:00
using osu.Game.Overlays.Chat ;
2021-02-02 14:44:11 +08:00
using osuTK.Input ;
2018-12-20 15:50:38 +08:00
2019-03-25 00:02:36 +08:00
namespace osu.Game.Tests.Visual.Online
2018-12-20 15:50:38 +08:00
{
2021-02-02 14:44:11 +08:00
public class TestSceneStandAloneChatDisplay : OsuManualInputManagerTestScene
2018-12-20 15:50:38 +08:00
{
2021-11-04 17:02:44 +08:00
private readonly APIUser admin = new APIUser
2018-12-20 15:50:38 +08:00
{
Username = "HappyStick" ,
Id = 2 ,
Colour = "f2ca34"
} ;
2021-11-04 17:02:44 +08:00
private readonly APIUser redUser = new APIUser
2018-12-20 15:50:38 +08:00
{
Username = "BanchoBot" ,
Id = 3 ,
} ;
2021-11-04 17:02:44 +08:00
private readonly APIUser blueUser = new APIUser
2018-12-20 15:50:38 +08:00
{
Username = "Zallius" ,
Id = 4 ,
} ;
2021-11-04 17:02:44 +08:00
private readonly APIUser longUsernameUser = new APIUser
2019-10-01 21:47:53 +08:00
{
Username = "Very Long Long Username" ,
Id = 5 ,
} ;
2022-06-08 17:54:23 +08:00
private ChannelManager channelManager ;
2018-12-20 17:21:37 +08:00
2021-02-01 04:44:27 +08:00
private TestStandAloneChatDisplay chatDisplay ;
private int messageIdSequence ;
private Channel testChannel ;
2018-12-20 17:21:37 +08:00
2022-06-08 17:54:23 +08:00
protected override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
2018-12-20 15:50:38 +08:00
{
2022-10-28 17:08:08 +08:00
var api = parent . Get < IAPIProvider > ( ) ;
2022-11-04 17:51:00 +08:00
Add ( channelManager = new ChannelManager ( api ) ) ;
2022-06-08 17:54:23 +08:00
var dependencies = new DependencyContainer ( base . CreateChildDependencies ( parent ) ) ;
dependencies . Cache ( channelManager ) ;
return dependencies ;
2018-12-20 15:50:38 +08:00
}
2021-02-01 04:44:27 +08:00
[SetUp]
public void SetUp ( ) = > Schedule ( ( ) = >
2018-12-20 15:50:38 +08:00
{
2021-02-01 04:44:27 +08:00
messageIdSequence = 0 ;
channelManager . CurrentChannel . Value = testChannel = new Channel ( ) ;
2018-12-20 17:21:37 +08:00
2021-02-01 04:44:27 +08:00
Children = new [ ]
{
chatDisplay = new TestStandAloneChatDisplay
{
Anchor = Anchor . CentreLeft ,
Origin = Anchor . CentreLeft ,
Margin = new MarginPadding ( 20 ) ,
Size = new Vector2 ( 400 , 80 ) ,
Channel = { Value = testChannel } ,
} ,
2021-02-02 03:38:42 +08:00
new TestStandAloneChatDisplay ( true )
2021-02-01 04:44:27 +08:00
{
Anchor = Anchor . CentreRight ,
Origin = Anchor . CentreRight ,
Margin = new MarginPadding ( 20 ) ,
Size = new Vector2 ( 400 , 150 ) ,
Channel = { Value = testChannel } ,
}
} ;
} ) ;
2018-12-21 16:54:12 +08:00
2021-04-22 13:51:14 +08:00
[Test]
public void TestSystemMessageOrdering ( )
{
var standardMessage = new Message ( messageIdSequence + + )
{
Sender = admin ,
Content = "I am a wang!"
} ;
var infoMessage1 = new InfoMessage ( $"the system is calling {messageIdSequence++}" ) ;
var infoMessage2 = new InfoMessage ( $"the system is calling {messageIdSequence++}" ) ;
AddStep ( "message from admin" , ( ) = > testChannel . AddNewMessages ( standardMessage ) ) ;
AddStep ( "message from system" , ( ) = > testChannel . AddNewMessages ( infoMessage1 ) ) ;
AddStep ( "message from system" , ( ) = > testChannel . AddNewMessages ( infoMessage2 ) ) ;
AddAssert ( "message order is correct" , ( ) = > testChannel . Messages . Count = = 3
& & testChannel . Messages [ 0 ] = = standardMessage
& & testChannel . Messages [ 1 ] = = infoMessage1
& & testChannel . Messages [ 2 ] = = infoMessage2 ) ;
}
2021-02-01 04:44:27 +08:00
[Test]
public void TestManyMessages ( )
{
2022-03-05 04:27:09 +08:00
sendRegularMessages ( ) ;
2021-02-02 14:44:11 +08:00
checkScrolledToBottom ( ) ;
2019-10-29 13:32:38 +08:00
const int messages_per_call = 10 ;
AddRepeatStep ( "add many messages" , ( ) = >
2020-02-15 10:54:29 +08:00
{
for ( int i = 0 ; i < messages_per_call ; i + + )
2019-10-29 13:32:38 +08:00
{
2021-02-01 04:44:27 +08:00
testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
2019-11-11 19:53:22 +08:00
{
2020-02-15 10:54:29 +08:00
Sender = longUsernameUser ,
Content = "Many messages! " + Guid . NewGuid ( ) ,
Timestamp = DateTimeOffset . Now
} ) ;
}
} , Channel . MAX_HISTORY / messages_per_call + 5 ) ;
2019-10-29 13:32:38 +08:00
2019-10-29 14:27:08 +08:00
AddAssert ( "Ensure no adjacent day separators" , ( ) = >
{
2022-06-04 23:11:49 +08:00
var indices = chatDisplay . FillFlow . OfType < DaySeparator > ( ) . Select ( ds = > chatDisplay . FillFlow . IndexOf ( ds ) ) ;
2019-10-29 14:27:08 +08:00
2021-10-27 12:04:41 +08:00
foreach ( int i in indices )
2019-11-11 19:53:22 +08:00
{
2022-06-04 23:11:49 +08:00
if ( i < chatDisplay . FillFlow . Count & & chatDisplay . FillFlow [ i + 1 ] is DaySeparator )
2019-10-29 14:27:08 +08:00
return false ;
2019-11-11 19:53:22 +08:00
}
2019-10-29 14:27:08 +08:00
return true ;
} ) ;
2021-02-02 14:44:11 +08:00
checkScrolledToBottom ( ) ;
2019-10-29 13:32:38 +08:00
}
2022-03-05 04:27:09 +08:00
[Test]
public void TestMessageHighlighting ( )
{
Message highlighted = null ;
sendRegularMessages ( ) ;
AddStep ( "highlight first message" , ( ) = >
{
highlighted = testChannel . Messages [ 0 ] ;
2022-03-10 07:49:23 +08:00
testChannel . HighlightedMessage . Value = highlighted ;
2022-03-05 04:27:09 +08:00
} ) ;
AddUntilStep ( "chat scrolled to first message" , ( ) = >
{
var line = chatDisplay . ChildrenOfType < ChatLine > ( ) . Single ( c = > c . Message = = highlighted ) ;
return chatDisplay . ScrollContainer . ScreenSpaceDrawQuad . Contains ( line . ScreenSpaceDrawQuad . Centre ) ;
} ) ;
sendMessage ( ) ;
checkNotScrolledToBottom ( ) ;
AddStep ( "highlight last message" , ( ) = >
{
highlighted = testChannel . Messages [ ^ 1 ] ;
2022-03-10 07:49:23 +08:00
testChannel . HighlightedMessage . Value = highlighted ;
2022-03-05 04:27:09 +08:00
} ) ;
AddUntilStep ( "chat scrolled to last message" , ( ) = >
{
var line = chatDisplay . ChildrenOfType < ChatLine > ( ) . Single ( c = > c . Message = = highlighted ) ;
return chatDisplay . ScrollContainer . ScreenSpaceDrawQuad . Contains ( line . ScreenSpaceDrawQuad . Centre ) ;
} ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
AddRepeatStep ( "highlight other random messages" , ( ) = >
{
highlighted = testChannel . Messages [ RNG . Next ( 0 , testChannel . Messages . Count - 1 ) ] ;
2022-03-10 07:49:23 +08:00
testChannel . HighlightedMessage . Value = highlighted ;
2022-03-05 04:27:09 +08:00
} , 10 ) ;
}
[Test]
public void TestMessageHighlightingOnFilledChat ( )
{
2022-03-10 07:49:23 +08:00
int index = 0 ;
2022-03-10 09:01:07 +08:00
fillChat ( 100 ) ;
2022-03-05 04:27:09 +08:00
2022-03-10 07:49:23 +08:00
AddStep ( "highlight first message" , ( ) = > testChannel . HighlightedMessage . Value = testChannel . Messages [ index = 0 ] ) ;
AddStep ( "highlight next message" , ( ) = > testChannel . HighlightedMessage . Value = testChannel . Messages [ index = Math . Min ( index + 1 , testChannel . Messages . Count - 1 ) ] ) ;
AddStep ( "highlight last message" , ( ) = > testChannel . HighlightedMessage . Value = testChannel . Messages [ index = testChannel . Messages . Count - 1 ] ) ;
AddStep ( "highlight previous message" , ( ) = > testChannel . HighlightedMessage . Value = testChannel . Messages [ index = Math . Max ( index - 1 , 0 ) ] ) ;
AddRepeatStep ( "highlight random messages" , ( ) = > testChannel . HighlightedMessage . Value = testChannel . Messages [ index = RNG . Next ( 0 , testChannel . Messages . Count - 1 ) ] , 10 ) ;
2022-03-05 04:27:09 +08:00
}
2021-02-01 04:44:27 +08:00
/// <summary>
/// Tests that when a message gets wrapped by the chat display getting contracted while scrolled to bottom, the chat will still keep scrolling down.
/// </summary>
[Test]
public void TestMessageWrappingKeepsAutoScrolling ( )
2021-02-02 14:44:11 +08:00
{
fillChat ( ) ;
// send message with short words for text wrapping to occur when contracting chat.
sendMessage ( ) ;
AddStep ( "contract chat" , ( ) = > chatDisplay . Width - = 100 ) ;
checkScrolledToBottom ( ) ;
AddStep ( "send another message" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = admin ,
Content = "As we were saying..." ,
} ) ) ;
checkScrolledToBottom ( ) ;
}
[Test]
2022-03-05 04:23:58 +08:00
public void TestOverrideChatScrolling ( )
{
fillChat ( ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
AddStep ( "Scroll to start" , ( ) = > chatDisplay . ScrollContainer . ScrollToStart ( ) ) ;
checkNotScrolledToBottom ( ) ;
sendMessage ( ) ;
checkNotScrolledToBottom ( ) ;
AddStep ( "Scroll to bottom" , ( ) = > chatDisplay . ScrollContainer . ScrollToEnd ( ) ) ;
checkScrolledToBottom ( ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
}
[Test]
public void TestOverrideChatScrollingByUser ( )
2021-02-02 14:44:11 +08:00
{
fillChat ( ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
AddStep ( "User scroll up" , ( ) = >
{
InputManager . MoveMouseTo ( chatDisplay . ScreenSpaceDrawQuad . Centre ) ;
InputManager . PressButton ( MouseButton . Left ) ;
InputManager . MoveMouseTo ( chatDisplay . ScreenSpaceDrawQuad . Centre + new Vector2 ( 0 , chatDisplay . ScreenSpaceDrawQuad . Height ) ) ;
InputManager . ReleaseButton ( MouseButton . Left ) ;
} ) ;
checkNotScrolledToBottom ( ) ;
sendMessage ( ) ;
checkNotScrolledToBottom ( ) ;
AddRepeatStep ( "User scroll to bottom" , ( ) = >
{
InputManager . MoveMouseTo ( chatDisplay . ScreenSpaceDrawQuad . Centre ) ;
InputManager . PressButton ( MouseButton . Left ) ;
InputManager . MoveMouseTo ( chatDisplay . ScreenSpaceDrawQuad . Centre - new Vector2 ( 0 , chatDisplay . ScreenSpaceDrawQuad . Height ) ) ;
InputManager . ReleaseButton ( MouseButton . Left ) ;
} , 5 ) ;
checkScrolledToBottom ( ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
}
2021-02-02 14:46:26 +08:00
[Test]
public void TestLocalEchoMessageResetsScroll ( )
{
fillChat ( ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
AddStep ( "User scroll up" , ( ) = >
{
InputManager . MoveMouseTo ( chatDisplay . ScreenSpaceDrawQuad . Centre ) ;
InputManager . PressButton ( MouseButton . Left ) ;
InputManager . MoveMouseTo ( chatDisplay . ScreenSpaceDrawQuad . Centre + new Vector2 ( 0 , chatDisplay . ScreenSpaceDrawQuad . Height ) ) ;
InputManager . ReleaseButton ( MouseButton . Left ) ;
} ) ;
checkNotScrolledToBottom ( ) ;
sendMessage ( ) ;
checkNotScrolledToBottom ( ) ;
sendLocalMessage ( ) ;
checkScrolledToBottom ( ) ;
sendMessage ( ) ;
checkScrolledToBottom ( ) ;
}
2022-03-10 09:01:07 +08:00
private void fillChat ( int count = 10 )
2021-02-01 04:44:27 +08:00
{
AddStep ( "fill chat" , ( ) = >
{
2022-03-10 09:01:07 +08:00
for ( int i = 0 ; i < count ; i + + )
2021-02-01 04:44:27 +08:00
{
testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = longUsernameUser ,
Content = $"some stuff {Guid.NewGuid()}" ,
} ) ;
}
} ) ;
2021-02-02 14:44:11 +08:00
checkScrolledToBottom ( ) ;
}
2021-02-01 04:44:27 +08:00
2021-02-02 14:44:11 +08:00
private void sendMessage ( )
{
2021-02-01 04:44:27 +08:00
AddStep ( "send lorem ipsum" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = longUsernameUser ,
Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce et bibendum velit." ,
} ) ) ;
2021-02-02 14:44:11 +08:00
}
2021-02-01 04:44:27 +08:00
2021-02-02 14:46:26 +08:00
private void sendLocalMessage ( )
{
2021-02-02 15:11:13 +08:00
AddStep ( "send local echo" , ( ) = > testChannel . AddLocalEcho ( new LocalEchoMessage
2021-02-02 14:46:26 +08:00
{
Sender = longUsernameUser ,
Content = "This is a local echo message." ,
} ) ) ;
}
2022-03-05 04:27:09 +08:00
private void sendRegularMessages ( )
{
AddStep ( "message from admin" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = admin ,
Content = "I am a wang!"
} ) ) ;
AddStep ( "message from team red" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = redUser ,
Content = "I am team red."
} ) ) ;
AddStep ( "message from team red" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = redUser ,
Content = "I plan to win!"
} ) ) ;
AddStep ( "message from team blue" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = blueUser ,
Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand."
} ) ) ;
AddStep ( "message from admin" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = admin ,
Content = "Okay okay, calm down guys. Let's do this!"
} ) ) ;
AddStep ( "message from long username" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = longUsernameUser ,
Content = "Hi guys, my new username is lit!"
} ) ) ;
AddStep ( "message with new date" , ( ) = > testChannel . AddNewMessages ( new Message ( messageIdSequence + + )
{
Sender = longUsernameUser ,
Content = "Message from the future!" ,
Timestamp = DateTimeOffset . Now
} ) ) ;
}
2021-02-02 14:44:11 +08:00
private void checkScrolledToBottom ( ) = >
AddUntilStep ( "is scrolled to bottom" , ( ) = > chatDisplay . ScrolledToBottom ) ;
2021-02-01 04:44:27 +08:00
2021-02-02 14:44:11 +08:00
private void checkNotScrolledToBottom ( ) = >
AddUntilStep ( "not scrolled to bottom" , ( ) = > ! chatDisplay . ScrolledToBottom ) ;
2021-02-01 04:44:27 +08:00
2019-10-29 13:32:38 +08:00
private class TestStandAloneChatDisplay : StandAloneChatDisplay
{
2021-12-10 13:15:00 +08:00
public TestStandAloneChatDisplay ( bool textBox = false )
: base ( textBox )
2019-10-29 13:32:38 +08:00
{
}
2022-03-05 04:23:58 +08:00
public DrawableChannel DrawableChannel = > InternalChildren . OfType < DrawableChannel > ( ) . First ( ) ;
2019-10-29 14:27:08 +08:00
2022-03-07 04:34:12 +08:00
public ChannelScrollContainer ScrollContainer = > ( ChannelScrollContainer ) ( ( Container ) DrawableChannel . Child ) . Child ;
2019-10-29 14:27:08 +08:00
public FillFlowContainer FillFlow = > ( FillFlowContainer ) ScrollContainer . Child ;
public bool ScrolledToBottom = > ScrollContainer . IsScrolledToEnd ( 1 ) ;
2018-12-20 15:50:38 +08:00
}
}
}