diff --git a/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj b/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj index e7c03aa3..b93b6e13 100644 --- a/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj +++ b/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj @@ -5,6 +5,7 @@ true true opencover + $(NoWarn);xUnit1013 diff --git a/DiscordChatExporter.Cli.Tests/EmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/EmbedSpecs.cs index 2ffee94a..529c3588 100644 --- a/DiscordChatExporter.Cli.Tests/EmbedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/EmbedSpecs.cs @@ -1,69 +1,26 @@ -using System; -using System.IO; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using AngleSharp.Dom; -using CliFx.Infrastructure; -using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Fixtures; -using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Cli.Tests.TestData; -using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Discord; -using DiscordChatExporter.Core.Exporting; using FluentAssertions; -using JsonExtensions; using Xunit; -using Xunit.Abstractions; namespace DiscordChatExporter.Cli.Tests { - public class EmbedSpecs : IClassFixture + public record EmbedSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture { - private readonly ITestOutputHelper _testOutput; - private readonly TempOutputFixture _tempOutput; - - public EmbedSpecs(ITestOutputHelper testOutput, TempOutputFixture tempOutput) - { - _testOutput = testOutput; - _tempOutput = tempOutput; - } - [Fact] public async Task Message_with_an_embed_is_rendered_correctly_in_JSON() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("json"); - // Act - var jsonData = await GlobalCache.WrapAsync("embed-specs-output-json", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.EmbedTestCases)}, - ExportFormat = ExportFormat.Json, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("866769910729146400") + ); - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(jsonData); - - var json = Json.Parse(jsonData); - - var messageJson = json - .GetProperty("messages") - .EnumerateArray() - .Single(j => string.Equals( - j.GetProperty("id").GetString(), - "866769910729146400", - StringComparison.OrdinalIgnoreCase - )); - - var embed = messageJson + var embed = message .GetProperty("embeds") .EnumerateArray() .Single(); @@ -102,33 +59,14 @@ namespace DiscordChatExporter.Cli.Tests [Fact] public async Task Message_with_an_embed_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("embed-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.EmbedTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866769910729146400"); - var messageText = messageHtml?.Text(); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("866769910729146400") + ); // Assert - messageText.Should().ContainAll( + message.Text().Should().ContainAll( "Embed author", "Embed title", "Embed description", @@ -142,30 +80,13 @@ namespace DiscordChatExporter.Cli.Tests [Fact] public async Task Message_with_a_Spotify_track_is_rendered_using_an_iframe_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("embed-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.EmbedTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("867886632203976775") + ); - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-867886632203976775"); - var iframeHtml = messageHtml?.QuerySelector("iframe"); + var iframeHtml = message.QuerySelector("iframe"); // Assert iframeHtml.Should().NotBeNull(); @@ -176,30 +97,13 @@ namespace DiscordChatExporter.Cli.Tests [Fact] public async Task Message_with_a_YouTube_video_is_rendered_using_an_iframe_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("embed-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.EmbedTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("866472508588294165") + ); - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866472508588294165"); - var iframeHtml = messageHtml?.QuerySelector("iframe"); + var iframeHtml = message.QuerySelector("iframe"); // Assert iframeHtml.Should().NotBeNull(); diff --git a/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs b/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs new file mode 100644 index 00000000..27cfb75e --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Fixtures/ExportWrapperFixture.cs @@ -0,0 +1,120 @@ +using System; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using AngleSharp.Dom; +using AngleSharp.Html.Dom; +using CliFx.Infrastructure; +using DiscordChatExporter.Cli.Commands; +using DiscordChatExporter.Cli.Tests.Infra; +using DiscordChatExporter.Cli.Tests.Utils; +using DiscordChatExporter.Core.Discord; +using DiscordChatExporter.Core.Exporting; +using JsonExtensions; + +namespace DiscordChatExporter.Cli.Tests.Fixtures +{ + public class ExportWrapperFixture : IDisposable + { + private string DirPath { get; } = Path.Combine( + Path.GetDirectoryName(typeof(ExportWrapperFixture).Assembly.Location) ?? Directory.GetCurrentDirectory(), + "ExportCache", + Guid.NewGuid().ToString() + ); + + public async ValueTask ExportAsHtmlAsync(Snowflake channelId) + { + var filePath = Path.Combine(DirPath, channelId + ".html"); + + // 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.HtmlDark, + OutputPath = filePath + }.ExecuteAsync(new FakeConsole()); + } + + var data = await File.ReadAllTextAsync(filePath); + + return Html.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 ExportAsJsonAsync(Snowflake channelId) + { + var filePath = Path.Combine(DirPath, channelId + ".json"); + + // 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()); + } + + var data = await File.ReadAllTextAsync(filePath); + + return Json.Parse(data); + } + + public async ValueTask GetMessageAsJsonAsync(Snowflake channelId, Snowflake messageId) + { + var document = await ExportAsJsonAsync(channelId); + + var message = document + .GetProperty("messages") + .EnumerateArray() + .SingleOrDefault(j => string.Equals( + j.GetProperty("id").GetString(), + messageId.ToString(), + StringComparison.OrdinalIgnoreCase + )); + + if (message.ValueKind == JsonValueKind.Undefined) + { + throw new InvalidOperationException( + $"Message '{messageId}' does not exist in export of channel '{channelId}'." + ); + } + + return message; + } + + public void Dispose() + { + try + { + Directory.Delete(DirPath, true); + } + catch (DirectoryNotFoundException) + { + } + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Fixtures/TempOutputFixture.cs b/DiscordChatExporter.Cli.Tests/Fixtures/TempOutputFixture.cs deleted file mode 100644 index f2991e08..00000000 --- a/DiscordChatExporter.Cli.Tests/Fixtures/TempOutputFixture.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.IO; - -namespace DiscordChatExporter.Cli.Tests.Fixtures -{ - public class TempOutputFixture : IDisposable - { - public string DirPath => Path.Combine( - Path.GetDirectoryName(typeof(TempOutputFixture).Assembly.Location) ?? Directory.GetCurrentDirectory(), - "Temp", - Guid.NewGuid().ToString() - ); - - public TempOutputFixture() => Directory.CreateDirectory(DirPath); - - public string GetTempFilePath() => Path.Combine(DirPath, Guid.NewGuid().ToString()); - - public string GetTempFilePath(string extension) => Path.ChangeExtension(GetTempFilePath(), extension); - - public void Dispose() - { - try - { - Directory.Delete(DirPath, true); - } - catch (DirectoryNotFoundException) - { - } - } - } -} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/MentionSpecs.cs b/DiscordChatExporter.Cli.Tests/MentionSpecs.cs index 06716ce8..9c214a6e 100644 --- a/DiscordChatExporter.Cli.Tests/MentionSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/MentionSpecs.cs @@ -1,72 +1,29 @@ -using System; -using System.IO; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using AngleSharp.Dom; -using CliFx.Infrastructure; -using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Fixtures; -using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Cli.Tests.TestData; -using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Discord; -using DiscordChatExporter.Core.Exporting; using FluentAssertions; -using JsonExtensions; using Xunit; -using Xunit.Abstractions; namespace DiscordChatExporter.Cli.Tests { - public class MentionSpecs : IClassFixture + public record MentionSpecs(ExportWrapperFixture ExportWrapper) : IClassFixture { - private readonly ITestOutputHelper _testOutput; - private readonly TempOutputFixture _tempOutput; - - public MentionSpecs(ITestOutputHelper testOutput, TempOutputFixture tempOutput) - { - _testOutput = testOutput; - _tempOutput = tempOutput; - } - [Fact] public async Task User_mention_is_rendered_correctly_in_JSON() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("json"); - // Act - var jsonData = await GlobalCache.WrapAsync("mention-specs-output-json", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.Json, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(jsonData); - - var json = Json.Parse(jsonData); - - var messageJson = json - .GetProperty("messages") - .EnumerateArray() - .Single(j => string.Equals( - j.GetProperty("id").GetString(), - "866458840245076028", - StringComparison.OrdinalIgnoreCase - )); + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866458840245076028") + ); // Assert - messageJson.GetProperty("content").GetString().Should().Be("User mention: @Tyrrrz"); + message.GetProperty("content").GetString().Should().Be("User mention: @Tyrrrz"); - messageJson + message .GetProperty("mentions") .EnumerateArray() .Select(j => j.GetProperty("id").GetString()) @@ -76,244 +33,93 @@ namespace DiscordChatExporter.Cli.Tests [Fact] public async Task User_mention_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("mention-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866458840245076028"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866458840245076028") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("User mention: @Tyrrrz"); - messageHtml?.InnerHtml.Should().Contain("Tyrrrz#5447"); + 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() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("json"); - // Act - var jsonData = await GlobalCache.WrapAsync("mention-specs-output-json", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.Json, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(jsonData); - - var json = Json.Parse(jsonData); - - var messageJson = json - .GetProperty("messages") - .EnumerateArray() - .Single(j => string.Equals( - j.GetProperty("id").GetString(), - "866459040480624680", - StringComparison.OrdinalIgnoreCase - )); + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459040480624680") + ); // Assert - messageJson.GetProperty("content").GetString().Should().Be("Text channel mention: #mention-tests"); + message.GetProperty("content").GetString().Should().Be("Text channel mention: #mention-tests"); } [Fact] public async Task Text_channel_mention_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("mention-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866459040480624680"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459040480624680") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("Text channel mention: #mention-tests"); + message.Text().Trim().Should().Be("Text channel mention: #mention-tests"); } [Fact] public async Task Voice_channel_mention_is_rendered_correctly_in_JSON() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("json"); - // Act - var jsonData = await GlobalCache.WrapAsync("mention-specs-output-json", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.Json, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(jsonData); - - var json = Json.Parse(jsonData); - - var messageJson = json - .GetProperty("messages") - .EnumerateArray() - .Single(j => string.Equals( - j.GetProperty("id").GetString(), - "866459175462633503", - StringComparison.OrdinalIgnoreCase - )); + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459175462633503") + ); // Assert - messageJson.GetProperty("content").GetString().Should().Be("Voice channel mention: #chaos-vc [voice]"); + message.GetProperty("content").GetString().Should().Be("Voice channel mention: #chaos-vc [voice]"); } [Fact] public async Task Voice_channel_mention_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("mention-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866459175462633503"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459175462633503") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("Voice channel mention: 🔊chaos-vc"); + message.Text().Trim().Should().Be("Voice channel mention: 🔊chaos-vc"); } [Fact] public async Task Role_mention_is_rendered_correctly_in_JSON() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("json"); - // Act - var jsonData = await GlobalCache.WrapAsync("mention-specs-output-json", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.Json, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(jsonData); - - var json = Json.Parse(jsonData); - - var messageJson = json - .GetProperty("messages") - .EnumerateArray() - .Single(j => string.Equals( - j.GetProperty("id").GetString(), - "866459254693429258", - StringComparison.OrdinalIgnoreCase - )); + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459254693429258") + ); // Assert - messageJson.GetProperty("content").GetString().Should().Be("Role mention: @Role 1"); + message.GetProperty("content").GetString().Should().Be("Role mention: @Role 1"); } [Fact] public async Task Role_mention_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("mention-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.MentionTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866459254693429258"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("866459254693429258") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("Role mention: @Role 1"); + 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/ReplySpecs.cs index 42e7bf81..ab6a27f0 100644 --- a/DiscordChatExporter.Cli.Tests/ReplySpecs.cs +++ b/DiscordChatExporter.Cli.Tests/ReplySpecs.cs @@ -1,129 +1,56 @@ -using System.IO; -using System.Threading.Tasks; +using System.Threading.Tasks; using AngleSharp.Dom; -using CliFx.Infrastructure; -using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Fixtures; -using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Cli.Tests.TestData; -using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Discord; -using DiscordChatExporter.Core.Exporting; using FluentAssertions; using Xunit; -using Xunit.Abstractions; namespace DiscordChatExporter.Cli.Tests { - public class ReplySpecs : IClassFixture + public record ReplySpecs(ExportWrapperFixture ExportWrapper) : IClassFixture { - private readonly ITestOutputHelper _testOutput; - private readonly TempOutputFixture _tempOutput; - - public ReplySpecs(ITestOutputHelper testOutput, TempOutputFixture tempOutput) - { - _testOutput = testOutput; - _tempOutput = tempOutput; - } - [Fact] public async Task Reply_to_a_normal_message_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("reply-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.ReplyTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866460738239725598"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.ReplyTestCases, + Snowflake.Parse("866460738239725598") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("reply to original"); - messageHtml?.QuerySelector(".chatlog__reference-link")?.Text().Trim().Should().Be("original"); + message.Text().Trim().Should().Be("reply to original"); + message.QuerySelector(".chatlog__reference-link")?.Text().Trim().Should().Be("original"); } [Fact] public async Task Reply_to_a_deleted_message_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("reply-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.ReplyTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866460975388819486"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.ReplyTestCases, + Snowflake.Parse("866460975388819486") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("reply to deleted"); - messageHtml?.QuerySelector(".chatlog__reference-link")?.Text().Trim().Should() + message.Text().Trim().Should().Be("reply to deleted"); + message.QuerySelector(".chatlog__reference-link")?.Text().Trim().Should() .Be("Original message was deleted or could not be loaded."); } [Fact] public async Task Reply_to_a_empty_message_with_attachment_is_rendered_correctly_in_HTML() { - // Arrange - var outputFilePath = _tempOutput.GetTempFilePath("html"); - // Act - var htmlData = await GlobalCache.WrapAsync("reply-specs-output-html", async () => - { - await new ExportChannelsCommand - { - TokenValue = Secrets.DiscordToken, - IsBotToken = Secrets.IsDiscordTokenBot, - ChannelIds = new[] {Snowflake.Parse(ChannelIds.ReplyTestCases)}, - ExportFormat = ExportFormat.HtmlDark, - OutputPath = outputFilePath - }.ExecuteAsync(new FakeConsole()); - - return await File.ReadAllTextAsync(outputFilePath); - }); - - _testOutput.WriteLine(htmlData); - - var html = Html.Parse(htmlData); - - var messageHtml = html.QuerySelector("#message-866462470335627294"); + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.ReplyTestCases, + Snowflake.Parse("866462470335627294") + ); // Assert - messageHtml.Should().NotBeNull(); - messageHtml?.Text().Trim().Should().Be("reply to attachment"); - messageHtml?.QuerySelector(".chatlog__reference-link")?.Text().Trim().Should() + message.Text().Trim().Should().Be("reply to attachment"); + message.QuerySelector(".chatlog__reference-link")?.Text().Trim().Should() .Be("Click to see attachment 🖼️"); } } diff --git a/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs b/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs index e06fed13..582a6bf0 100644 --- a/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs +++ b/DiscordChatExporter.Cli.Tests/TestData/ChannelIds.cs @@ -1,11 +1,13 @@ -namespace DiscordChatExporter.Cli.Tests.TestData +using DiscordChatExporter.Core.Discord; + +namespace DiscordChatExporter.Cli.Tests.TestData { public static class ChannelIds { - public static string EmbedTestCases => "866472452459462687"; + public static Snowflake EmbedTestCases { get; } = Snowflake.Parse("866472452459462687"); - public static string MentionTestCases => "866458801389174794"; + public static Snowflake MentionTestCases { get; } = Snowflake.Parse("866458801389174794"); - public static string ReplyTestCases => "866459871934677052"; + public static Snowflake ReplyTestCases { get; } = Snowflake.Parse("866459871934677052"); } } \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Utils/GlobalCache.cs b/DiscordChatExporter.Cli.Tests/Utils/GlobalCache.cs deleted file mode 100644 index 5e357b9d..00000000 --- a/DiscordChatExporter.Cli.Tests/Utils/GlobalCache.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Threading.Tasks; - -namespace DiscordChatExporter.Cli.Tests.Utils -{ - internal static class GlobalCache - { - private static readonly ConcurrentDictionary Dictionary = new(); - - public static async Task WrapAsync(string key, Func> getAsync) - { - if (Dictionary.TryGetValue(key, out var value) && value is T existing) - return existing; - - var result = await getAsync(); - Dictionary[key] = result; - - return result; - } - } -} \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Utils/Pollyfills.cs b/DiscordChatExporter.Cli.Tests/Utils/Pollyfills.cs new file mode 100644 index 00000000..2e592b8d --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Utils/Pollyfills.cs @@ -0,0 +1,9 @@ +// ReSharper disable CheckNamespace +// TODO: remove after moving to .NET 5 + +namespace System.Runtime.CompilerServices +{ + internal static class IsExternalInit + { + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs index 6f85b079..40d0d777 100644 --- a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs +++ b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs @@ -22,15 +22,13 @@ namespace DiscordChatExporter.Cli.Utils.Extensions ? new NoopExclusivityMode() : null; - return AnsiConsole.Create( - new AnsiConsoleSettings - { - Ansi = AnsiSupport.Detect, - ColorSystem = ColorSystemSupport.Detect, - Out = new AnsiConsoleOutput(console.Output), - ExclusivityMode = exclusivityMode - } - ); + return AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Detect, + ColorSystem = ColorSystemSupport.Detect, + Out = new AnsiConsoleOutput(console.Output), + ExclusivityMode = exclusivityMode + }); } public static Progress CreateProgressTicker(this IConsole console) => console