// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Overlays; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Comments; using osu.Game.Overlays.Comments.Buttons; namespace osu.Game.Tests.Visual.Online { [TestFixture] public partial class TestSceneCommentsContainer : OsuTestScene { [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; private CommentsContainer commentsContainer = null!; private TextBox editorTextBox = null!; [SetUp] public void SetUp() => Schedule(() => { Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, Child = commentsContainer = new CommentsContainer() }; editorTextBox = commentsContainer.ChildrenOfType().First(); }); [Test] public void TestIdleState() { AddUntilStep("loading spinner shown", () => commentsContainer.ChildrenOfType().Single().IsLoading); } [TestCase(false)] [TestCase(true)] public void TestSingleCommentsPage(bool withPinned) { setUpCommentsResponse(getExampleComments(withPinned)); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("show more button hidden", () => commentsContainer.ChildrenOfType().Single().Alpha == 0); if (withPinned) AddAssert("pinned comment replies collapsed", () => commentsContainer.ChildrenOfType().First().Expanded.Value, () => Is.False); else AddAssert("first comment replies expanded", () => commentsContainer.ChildrenOfType().First().Expanded.Value, () => Is.True); } [TestCase(false)] [TestCase(true)] public void TestMultipleCommentPages(bool withPinned) { var comments = getExampleComments(withPinned); comments.HasMore = true; comments.TopLevelCount = 10; setUpCommentsResponse(comments); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("show more button visible", () => commentsContainer.ChildrenOfType().Single().Alpha == 1); } [TestCase(false)] [TestCase(true)] public void TestMultipleLoads(bool withPinned) { var comments = getExampleComments(withPinned); int topLevelCommentCount = comments.Comments.Count; AddStep("hide container", () => commentsContainer.Hide()); setUpCommentsResponse(comments); AddRepeatStep("show comments multiple times", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 456), 2); AddStep("show container", () => commentsContainer.Show()); AddUntilStep("comment count is correct", () => commentsContainer.ChildrenOfType().Count() == topLevelCommentCount); } [Test] public void TestNoComment() { var comments = getExampleComments(); comments.Comments.Clear(); setUpCommentsResponse(comments); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddAssert("no comment shown", () => !commentsContainer.ChildrenOfType().Any()); } [TestCase(false)] [TestCase(true)] public void TestSingleComment(bool withPinned) { var comment = new Comment { Id = 1, Message = "This is a single comment", LegacyName = "SingleUser", CreatedAt = DateTimeOffset.Now, VotesCount = 0, Pinned = withPinned, }; var bundle = new CommentBundle { Comments = new List { comment }, IncludedComments = new List(), PinnedComments = new List(), }; if (withPinned) bundle.PinnedComments.Add(comment); setUpCommentsResponse(bundle); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("wait comment load", () => commentsContainer.ChildrenOfType().Any()); AddAssert("only one comment shown", () => commentsContainer.ChildrenOfType().Count(d => d.Comment.Pinned == withPinned) == 1); } [Test] public void TestPost() { setUpCommentsResponse(new CommentBundle { Comments = new List() }); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddAssert("no comments placeholder shown", () => commentsContainer.ChildrenOfType().Any()); setUpPostResponse(); AddStep("enter text", () => editorTextBox.Current.Value = "comm"); AddStep("submit", () => commentsContainer.ChildrenOfType().First().TriggerClick()); AddUntilStep("comment sent", () => { string writtenText = editorTextBox.Current.Value; var comment = commentsContainer.ChildrenOfType().LastOrDefault(); return comment != null && comment.ChildrenOfType().Any(y => y.Text == writtenText); }); AddAssert("no comments placeholder removed", () => !commentsContainer.ChildrenOfType().Any()); } [Test] public void TestPostWithExistingComments() { setUpCommentsResponse(getExampleComments()); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("comments shown", () => commentsContainer.ChildrenOfType().Any()); setUpPostResponse(); AddStep("enter text", () => editorTextBox.Current.Value = "comm"); AddStep("submit", () => commentsContainer.ChildrenOfType().Single().ChildrenOfType().First().TriggerClick()); AddUntilStep("comment sent", () => { string writtenText = editorTextBox.Current.Value; var comment = commentsContainer.ChildrenOfType().LastOrDefault(); return comment != null && comment.ChildrenOfType().Any(y => y.Text == writtenText); }); } [Test] public void TestPostAsOwner() { setUpCommentsResponse(getExampleComments()); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("comments shown", () => commentsContainer.ChildrenOfType().Any()); setUpPostResponse(true); AddStep("enter text", () => editorTextBox.Current.Value = "comm"); AddStep("submit", () => commentsContainer.ChildrenOfType().Single().ChildrenOfType().First().TriggerClick()); AddUntilStep("comment sent", () => { string writtenText = editorTextBox.Current.Value; var comment = commentsContainer.ChildrenOfType().LastOrDefault(); return comment != null && comment.ChildrenOfType().Any(y => y.Text == writtenText) && comment.ChildrenOfType().Any(y => y.Text == "MAPPER"); }); } private void setUpCommentsResponse(CommentBundle commentBundle) => AddStep("set up response", () => { dummyAPI.HandleRequest = request => { if (!(request is GetCommentsRequest getCommentsRequest)) return false; getCommentsRequest.TriggerSuccess(commentBundle); return true; }; }); private void setUpPostResponse(bool asOwner = false) => AddStep("set up response", () => { dummyAPI.HandleRequest = request => { if (!(request is CommentPostRequest req)) return false; var bundle = new CommentBundle { Comments = new List { new Comment { Id = 98, Message = req.Message, LegacyName = "FirstUser", CreatedAt = DateTimeOffset.Now, VotesCount = 98, CommentableId = 2001, CommentableType = "test", } } }; if (asOwner) { bundle.Comments[0].UserId = 1001; bundle.Comments[0].User = new APIUser { Id = 1001, Username = "FirstUser" }; bundle.CommentableMeta.Add(new CommentableMeta { Id = 2001, OwnerId = 1001, OwnerTitle = "MAPPER", Type = "test", }); } req.TriggerSuccess(bundle); return true; }; }); private static CommentBundle getExampleComments(bool withPinned = false) { var bundle = new CommentBundle { Comments = new List { new Comment { Id = 1, Message = "This is a comment", LegacyName = "FirstUser", CreatedAt = DateTimeOffset.Now, VotesCount = 19, RepliesCount = 1 }, new Comment { Id = 5, ParentId = 1, Message = "This is a child comment", LegacyName = "SecondUser", CreatedAt = DateTimeOffset.Now, VotesCount = 4, }, new Comment { Id = 10, Message = "This is another comment", LegacyName = "ThirdUser", CreatedAt = DateTimeOffset.Now, VotesCount = 0 }, }, IncludedComments = new List(), PinnedComments = new List(), }; if (withPinned) { var pinnedComment = new Comment { Id = 15, Message = "This is pinned comment", LegacyName = "PinnedUser", CreatedAt = DateTimeOffset.Now, VotesCount = 999, Pinned = true, RepliesCount = 1, }; bundle.Comments.Add(pinnedComment); bundle.PinnedComments.Add(pinnedComment); bundle.Comments.Add(new Comment { Id = 20, Message = "Reply to pinned comment initially hidden", LegacyName = "AbandonedUser", CreatedAt = DateTimeOffset.Now, VotesCount = 0, ParentId = 15, }); } return bundle; } } }