diff --git a/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs b/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs index 27cfb75..31ca318 100644 --- a/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs +++ b/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs @@ -45,25 +45,51 @@ namespace DiscordChatExporter.Cli.Tests.Fixtures return Html.Parse(data); } - public async ValueTask GetMessageAsHtmlAsync(Snowflake channelId, Snowflake messageId) + public async ValueTask ExportAsJsonAsync(Snowflake channelId) { - var document = await ExportAsHtmlAsync(channelId); + var filePath = Path.Combine(DirPath, channelId + ".json"); - var message = document.QuerySelector("#message-" + messageId); + // Perform export only if it hasn't been done before + if (!File.Exists(filePath)) + { + await new ExportChannelsCommand + { + TokenValue = Secrets.DiscordToken, + IsBotToken = Secrets.IsDiscordTokenBot, + ChannelIds = new[] { channelId }, + ExportFormat = ExportFormat.Json, + OutputPath = filePath + }.ExecuteAsync(new FakeConsole()); + } - if (message is null) + var data = await File.ReadAllTextAsync(filePath); + + return Json.Parse(data); + } + + public async ValueTask ExportAsPlainTextAsync(Snowflake channelId) + { + var filePath = Path.Combine(DirPath, channelId + ".txt"); + + // Perform export only if it hasn't been done before + if (!File.Exists(filePath)) { - throw new InvalidOperationException( - $"Message '{messageId}' does not exist in export of channel '{channelId}'." - ); + await new ExportChannelsCommand + { + TokenValue = Secrets.DiscordToken, + IsBotToken = Secrets.IsDiscordTokenBot, + ChannelIds = new[] { channelId }, + ExportFormat = ExportFormat.PlainText, + OutputPath = filePath + }.ExecuteAsync(new FakeConsole()); } - return message; + return await File.ReadAllTextAsync(filePath); } - public async ValueTask ExportAsJsonAsync(Snowflake channelId) + public async ValueTask ExportAsCsvAsync(Snowflake channelId) { - var filePath = Path.Combine(DirPath, channelId + ".json"); + var filePath = Path.Combine(DirPath, channelId + ".csv"); // Perform export only if it hasn't been done before if (!File.Exists(filePath)) @@ -73,14 +99,28 @@ namespace DiscordChatExporter.Cli.Tests.Fixtures TokenValue = Secrets.DiscordToken, IsBotToken = Secrets.IsDiscordTokenBot, ChannelIds = new[] { channelId }, - ExportFormat = ExportFormat.Json, + ExportFormat = ExportFormat.Csv, OutputPath = filePath }.ExecuteAsync(new FakeConsole()); } - var data = await File.ReadAllTextAsync(filePath); + return await File.ReadAllTextAsync(filePath); + } - return Json.Parse(data); + public async ValueTask GetMessageAsHtmlAsync(Snowflake channelId, Snowflake messageId) + { + var document = await ExportAsHtmlAsync(channelId); + + var message = document.QuerySelector("#message-" + messageId); + + if (message is null) + { + throw new InvalidOperationException( + $"Message '{messageId}' does not exist in export of channel '{channelId}'." + ); + } + + return message; } public async ValueTask GetMessageAsJsonAsync(Snowflake channelId, Snowflake messageId) diff --git a/DiscordChatExporter.Cli.Tests/Specs/CsvWriting/GeneralSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/CsvWriting/GeneralSpecs.cs new file mode 100644 index 0000000..2dfd9fb --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/CsvWriting/GeneralSpecs.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using DiscordChatExporter.Cli.Tests.Fixtures; +using DiscordChatExporter.Cli.Tests.TestData; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs.CsvWriting +{ + public record GeneralSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture + { + [Fact] + public async Task Messages_are_exported_correctly() + { + // Act + var document = await ExportWrapper.ExportAsCsvAsync(ChannelIds.DateRangeTestCases); + + // Assert + document.Should().ContainAll( + "Tyrrrz#5447", + "Hello world", + "Goodbye world", + "Foo bar", + "Hurdle Durdle", + "One", + "Two", + "Three", + "Yeet" + ); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/EmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/EmbedSpecs.cs new file mode 100644 index 0000000..b3b6a19 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/EmbedSpecs.cs @@ -0,0 +1,68 @@ +using System.Threading.Tasks; +using AngleSharp.Dom; +using DiscordChatExporter.Cli.Tests.Fixtures; +using DiscordChatExporter.Cli.Tests.TestData; +using DiscordChatExporter.Core.Discord; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs.HtmlWriting +{ + public record EmbedSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture + { + [Fact] + public async Task Message_with_an_embed_is_rendered_correctly() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("866769910729146400") + ); + + // Assert + message.Text().Should().ContainAll( + "Embed author", + "Embed title", + "Embed description", + "Field 1", "Value 1", + "Field 2", "Value 2", + "Field 3", "Value 3", + "Embed footer" + ); + } + + [Fact] + public async Task Message_with_a_Spotify_track_is_rendered_using_an_iframe() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("867886632203976775") + ); + + var iframe = message.QuerySelector("iframe"); + + // Assert + iframe.Should().NotBeNull(); + iframe?.GetAttribute("src").Should() + .StartWithEquivalentOf("https://open.spotify.com/embed/track/1LHZMWefF9502NPfArRfvP"); + } + + [Fact] + public async Task Message_with_a_YouTube_video_is_rendered_using_an_iframe() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("866472508588294165") + ); + + var iframe = message.QuerySelector("iframe"); + + // Assert + iframe.Should().NotBeNull(); + iframe?.GetAttribute("src").Should() + .StartWithEquivalentOf("https://www.youtube.com/embed/qOWW4OlgbvE"); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/GeneralSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/GeneralSpecs.cs new file mode 100644 index 0000000..3b817d9 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/GeneralSpecs.cs @@ -0,0 +1,53 @@ +using System.Linq; +using System.Threading.Tasks; +using AngleSharp.Dom; +using DiscordChatExporter.Cli.Tests.Fixtures; +using DiscordChatExporter.Cli.Tests.TestData; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs.HtmlWriting +{ + public record GeneralSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture + { + [Fact] + public async Task Messages_are_exported_correctly() + { + // Act + var document = await ExportWrapper.ExportAsHtmlAsync(ChannelIds.DateRangeTestCases); + + var messageIds = document + .QuerySelectorAll(".chatlog__message") + .Select(e => e.GetAttribute("data-message-id")) + .ToArray(); + + var messageTexts = document + .QuerySelectorAll(".chatlog__content") + .Select(e => e.Text().Trim()) + .ToArray(); + + // Assert + messageIds.Should().Equal( + "866674314627121232", + "866710679758045195", + "866732113319428096", + "868490009366396958", + "868505966528835604", + "868505969821364245", + "868505973294268457", + "885169254029213696" + ); + + messageTexts.Should().Equal( + "Hello world", + "Goodbye world", + "Foo bar", + "Hurdle Durdle", + "One", + "Two", + "Three", + "Yeet" + ); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/MentionSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/MentionSpecs.cs new file mode 100644 index 0000000..0335da8 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/MentionSpecs.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using AngleSharp.Dom; +using DiscordChatExporter.Cli.Tests.Fixtures; +using DiscordChatExporter.Cli.Tests.TestData; +using DiscordChatExporter.Core.Discord; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs.HtmlWriting +{ + public record MentionSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture + { + [Fact] + public async Task User_mention_is_rendered_correctly() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866458840245076028") + ); + + // Assert + message.Text().Trim().Should().Be("User mention: @Tyrrrz"); + message.InnerHtml.Should().Contain("Tyrrrz#5447"); + } + + [Fact] + public async Task Text_channel_mention_is_rendered_correctly() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459040480624680") + ); + + // Assert + message.Text().Trim().Should().Be("Text channel mention: #mention-tests"); + } + + [Fact] + public async Task Voice_channel_mention_is_rendered_correctly() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459175462633503") + ); + + // Assert + message.Text().Trim().Should().Be("Voice channel mention: 🔊chaos-vc"); + } + + [Fact] + public async Task Role_mention_is_rendered_correctly() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459254693429258") + ); + + // Assert + message.Text().Trim().Should().Be("Role mention: @Role 1"); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/ReplySpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/ReplySpecs.cs similarity index 94% rename from DiscordChatExporter.Cli.Tests/ReplySpecs.cs rename to DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/ReplySpecs.cs index ab6a27f..6a3c702 100644 --- a/DiscordChatExporter.Cli.Tests/ReplySpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlWriting/ReplySpecs.cs @@ -6,12 +6,12 @@ using DiscordChatExporter.Core.Discord; using FluentAssertions; using Xunit; -namespace DiscordChatExporter.Cli.Tests +namespace DiscordChatExporter.Cli.Tests.Specs.HtmlWriting { public record ReplySpecs(ExportWrapperFixture ExportWrapper) : IClassFixture { [Fact] - public async Task Reply_to_a_normal_message_is_rendered_correctly_in_HTML() + public async Task Reply_to_a_normal_message_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -25,7 +25,7 @@ namespace DiscordChatExporter.Cli.Tests } [Fact] - public async Task Reply_to_a_deleted_message_is_rendered_correctly_in_HTML() + public async Task Reply_to_a_deleted_message_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -40,7 +40,7 @@ namespace DiscordChatExporter.Cli.Tests } [Fact] - public async Task Reply_to_a_empty_message_with_attachment_is_rendered_correctly_in_HTML() + public async Task Reply_to_a_empty_message_with_attachment_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( diff --git a/DiscordChatExporter.Cli.Tests/EmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/EmbedSpecs.cs similarity index 60% rename from DiscordChatExporter.Cli.Tests/EmbedSpecs.cs rename to DiscordChatExporter.Cli.Tests/Specs/JsonWriting/EmbedSpecs.cs index 529c358..83e7d95 100644 --- a/DiscordChatExporter.Cli.Tests/EmbedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/EmbedSpecs.cs @@ -1,18 +1,17 @@ using System.Linq; using System.Threading.Tasks; -using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Fixtures; using DiscordChatExporter.Cli.Tests.TestData; using DiscordChatExporter.Core.Discord; using FluentAssertions; using Xunit; -namespace DiscordChatExporter.Cli.Tests +namespace DiscordChatExporter.Cli.Tests.Specs.JsonWriting { public record EmbedSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture { [Fact] - public async Task Message_with_an_embed_is_rendered_correctly_in_JSON() + public async Task Message_with_an_embed_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -55,60 +54,5 @@ namespace DiscordChatExporter.Cli.Tests embedFields[2].GetProperty("value").GetString().Should().Be("Value 3"); embedFields[2].GetProperty("isInline").GetBoolean().Should().BeTrue(); } - - [Fact] - public async Task Message_with_an_embed_is_rendered_correctly_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.EmbedTestCases, - Snowflake.Parse("866769910729146400") - ); - - // Assert - message.Text().Should().ContainAll( - "Embed author", - "Embed title", - "Embed description", - "Field 1", "Value 1", - "Field 2", "Value 2", - "Field 3", "Value 3", - "Embed footer" - ); - } - - [Fact] - public async Task Message_with_a_Spotify_track_is_rendered_using_an_iframe_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.EmbedTestCases, - Snowflake.Parse("867886632203976775") - ); - - var iframeHtml = message.QuerySelector("iframe"); - - // Assert - iframeHtml.Should().NotBeNull(); - iframeHtml?.GetAttribute("src").Should() - .StartWithEquivalentOf("https://open.spotify.com/embed/track/1LHZMWefF9502NPfArRfvP"); - } - - [Fact] - public async Task Message_with_a_YouTube_video_is_rendered_using_an_iframe_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.EmbedTestCases, - Snowflake.Parse("866472508588294165") - ); - - var iframeHtml = message.QuerySelector("iframe"); - - // Assert - iframeHtml.Should().NotBeNull(); - iframeHtml?.GetAttribute("src").Should() - .StartWithEquivalentOf("https://www.youtube.com/embed/qOWW4OlgbvE"); - } } } \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/GeneralSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/GeneralSpecs.cs new file mode 100644 index 0000000..75b827a --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/GeneralSpecs.cs @@ -0,0 +1,54 @@ +using System.Linq; +using System.Threading.Tasks; +using DiscordChatExporter.Cli.Tests.Fixtures; +using DiscordChatExporter.Cli.Tests.TestData; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs.JsonWriting +{ + public record GeneralSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture + { + [Fact] + public async Task Messages_are_exported_correctly() + { + // Act + var document = await ExportWrapper.ExportAsJsonAsync(ChannelIds.DateRangeTestCases); + + var messageIds = document + .GetProperty("messages") + .EnumerateArray() + .Select(j => j.GetProperty("id").GetString()) + .ToArray(); + + var messageTexts = document + .GetProperty("messages") + .EnumerateArray() + .Select(j => j.GetProperty("content").GetString()) + .ToArray(); + + // Assert + messageIds.Should().Equal( + "866674314627121232", + "866710679758045195", + "866732113319428096", + "868490009366396958", + "868505966528835604", + "868505969821364245", + "868505973294268457", + "885169254029213696" + ); + + messageTexts.Should().Equal( + "Hello world", + "Goodbye world", + "Foo bar", + "Hurdle Durdle", + "One", + "Two", + "Three", + "Yeet" + ); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/MentionSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/MentionSpecs.cs similarity index 52% rename from DiscordChatExporter.Cli.Tests/MentionSpecs.cs rename to DiscordChatExporter.Cli.Tests/Specs/JsonWriting/MentionSpecs.cs index 9c214a6..e2d5cf0 100644 --- a/DiscordChatExporter.Cli.Tests/MentionSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonWriting/MentionSpecs.cs @@ -1,18 +1,17 @@ using System.Linq; using System.Threading.Tasks; -using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Fixtures; using DiscordChatExporter.Cli.Tests.TestData; using DiscordChatExporter.Core.Discord; using FluentAssertions; using Xunit; -namespace DiscordChatExporter.Cli.Tests +namespace DiscordChatExporter.Cli.Tests.Specs.JsonWriting { public record MentionSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture { [Fact] - public async Task User_mention_is_rendered_correctly_in_JSON() + public async Task User_mention_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -31,21 +30,7 @@ namespace DiscordChatExporter.Cli.Tests } [Fact] - public async Task User_mention_is_rendered_correctly_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.MentionTestCases, - Snowflake.Parse("866458840245076028") - ); - - // Assert - message.Text().Trim().Should().Be("User mention: @Tyrrrz"); - message.InnerHtml.Should().Contain("Tyrrrz#5447"); - } - - [Fact] - public async Task Text_channel_mention_is_rendered_correctly_in_JSON() + public async Task Text_channel_mention_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -58,20 +43,7 @@ namespace DiscordChatExporter.Cli.Tests } [Fact] - public async Task Text_channel_mention_is_rendered_correctly_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.MentionTestCases, - Snowflake.Parse("866459040480624680") - ); - - // Assert - message.Text().Trim().Should().Be("Text channel mention: #mention-tests"); - } - - [Fact] - public async Task Voice_channel_mention_is_rendered_correctly_in_JSON() + public async Task Voice_channel_mention_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -84,20 +56,7 @@ namespace DiscordChatExporter.Cli.Tests } [Fact] - public async Task Voice_channel_mention_is_rendered_correctly_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.MentionTestCases, - Snowflake.Parse("866459175462633503") - ); - - // Assert - message.Text().Trim().Should().Be("Voice channel mention: 🔊chaos-vc"); - } - - [Fact] - public async Task Role_mention_is_rendered_correctly_in_JSON() + public async Task Role_mention_is_rendered_correctly() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -108,18 +67,5 @@ namespace DiscordChatExporter.Cli.Tests // Assert message.GetProperty("content").GetString().Should().Be("Role mention: @Role 1"); } - - [Fact] - public async Task Role_mention_is_rendered_correctly_in_HTML() - { - // Act - var message = await ExportWrapper.GetMessageAsHtmlAsync( - ChannelIds.MentionTestCases, - Snowflake.Parse("866459254693429258") - ); - - // Assert - message.Text().Trim().Should().Be("Role mention: @Role 1"); - } } } \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/PlainTextWriting/GeneralSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PlainTextWriting/GeneralSpecs.cs new file mode 100644 index 0000000..4588458 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/PlainTextWriting/GeneralSpecs.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using DiscordChatExporter.Cli.Tests.Fixtures; +using DiscordChatExporter.Cli.Tests.TestData; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs.PlainTextWriting +{ + public record GeneralSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture + { + [Fact] + public async Task Messages_are_exported_correctly() + { + // Act + var document = await ExportWrapper.ExportAsPlainTextAsync(ChannelIds.DateRangeTestCases); + + // Assert + document.Should().ContainAll( + "Tyrrrz#5447", + "Hello world", + "Goodbye world", + "Foo bar", + "Hurdle Durdle", + "One", + "Two", + "Three", + "Yeet" + ); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs b/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs index 582a6bf..e698cf4 100644 --- a/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs +++ b/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs @@ -4,6 +4,8 @@ namespace DiscordChatExporter.Cli.Tests.TestData { public static class ChannelIds { + public static Snowflake DateRangeTestCases { get; } = Snowflake.Parse("866674248747319326"); + public static Snowflake EmbedTestCases { get; } = Snowflake.Parse("866472452459462687"); public static Snowflake MentionTestCases { get; } = Snowflake.Parse("866458801389174794");