Get rid of xUnit fixtures

pull/1002/head
Tyrrrz 1 year ago
parent 3487849eba
commit 53b8927fce

@ -1,8 +0,0 @@
using Xunit;
namespace DiscordChatExporter.Cli.Tests.Fixtures;
[CollectionDefinition(nameof(ExportWrapperCollection))]
public class ExportWrapperCollection : ICollectionFixture<ExportWrapperFixture>
{
}

@ -1,22 +0,0 @@
using System;
using System.IO;
using DiscordChatExporter.Cli.Tests.Utils;
namespace DiscordChatExporter.Cli.Tests.Fixtures;
public class TempOutputFixture : IDisposable
{
public string DirPath { get; } = Path.Combine(
Path.GetDirectoryName(typeof(TempOutputFixture).Assembly.Location) ?? Directory.GetCurrentDirectory(),
"Temp",
Guid.NewGuid().ToString()
);
public TempOutputFixture() => DirectoryEx.Reset(DirPath);
public string GetTempFilePath(string fileName) => Path.Combine(DirPath, fileName);
public string GetTempFilePath() => GetTempFilePath(Guid.NewGuid() + ".tmp");
public void Dispose() => DirectoryEx.DeleteIfExists(DirPath);
}

@ -2,31 +2,34 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
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;
namespace DiscordChatExporter.Cli.Tests.Infra;
public class ExportWrapperFixture : IDisposable
public static class ExportWrapper
{
private string DirPath { get; } = Path.Combine(
Path.GetDirectoryName(typeof(ExportWrapperFixture).Assembly.Location) ?? Directory.GetCurrentDirectory(),
"ExportCache",
Guid.NewGuid().ToString()
private static readonly string DirPath = Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory(),
"ExportCache"
);
public ExportWrapperFixture() => DirectoryEx.Reset(DirPath);
static ExportWrapper()
{
Directory.Delete(DirPath, true);
Directory.CreateDirectory(DirPath);
}
private async ValueTask<string> ExportAsync(Snowflake channelId, ExportFormat format)
private static async ValueTask<string> ExportAsync(Snowflake channelId, ExportFormat format)
{
var fileName = channelId.ToString() + '.' + format.GetFileExtension();
var filePath = Path.Combine(DirPath, fileName);
@ -55,32 +58,32 @@ public class ExportWrapperFixture : IDisposable
return await File.ReadAllTextAsync(filePath);
}
public async ValueTask<IHtmlDocument> ExportAsHtmlAsync(Snowflake channelId) => Html.Parse(
public static async ValueTask<IHtmlDocument> ExportAsHtmlAsync(Snowflake channelId) => Html.Parse(
await ExportAsync(channelId, ExportFormat.HtmlDark)
);
public async ValueTask<JsonElement> ExportAsJsonAsync(Snowflake channelId) => Json.Parse(
public static async ValueTask<JsonElement> ExportAsJsonAsync(Snowflake channelId) => Json.Parse(
await ExportAsync(channelId, ExportFormat.Json)
);
public async ValueTask<string> ExportAsPlainTextAsync(Snowflake channelId) =>
public static async ValueTask<string> ExportAsPlainTextAsync(Snowflake channelId) =>
await ExportAsync(channelId, ExportFormat.PlainText);
public async ValueTask<string> ExportAsCsvAsync(Snowflake channelId) =>
public static async ValueTask<string> ExportAsCsvAsync(Snowflake channelId) =>
await ExportAsync(channelId, ExportFormat.Csv);
public async ValueTask<IReadOnlyList<IElement>> GetMessagesAsHtmlAsync(Snowflake channelId) =>
public static async ValueTask<IReadOnlyList<IElement>> GetMessagesAsHtmlAsync(Snowflake channelId) =>
(await ExportAsHtmlAsync(channelId))
.QuerySelectorAll("[data-message-id]")
.ToArray();
public async ValueTask<IReadOnlyList<JsonElement>> GetMessagesAsJsonAsync(Snowflake channelId) =>
public static async ValueTask<IReadOnlyList<JsonElement>> GetMessagesAsJsonAsync(Snowflake channelId) =>
(await ExportAsJsonAsync(channelId))
.GetProperty("messages")
.EnumerateArray()
.ToArray();
public async ValueTask<IElement> GetMessageAsHtmlAsync(Snowflake channelId, Snowflake messageId)
public static async ValueTask<IElement> GetMessageAsHtmlAsync(Snowflake channelId, Snowflake messageId)
{
var message = (await GetMessagesAsHtmlAsync(channelId))
.SingleOrDefault(e =>
@ -101,7 +104,7 @@ public class ExportWrapperFixture : IDisposable
return message;
}
public async ValueTask<JsonElement> GetMessageAsJsonAsync(Snowflake channelId, Snowflake messageId)
public static async ValueTask<JsonElement> GetMessageAsJsonAsync(Snowflake channelId, Snowflake messageId)
{
var message = (await GetMessagesAsJsonAsync(channelId))
.SingleOrDefault(j =>
@ -121,6 +124,4 @@ public class ExportWrapperFixture : IDisposable
return message;
}
public void Dispose() => DirectoryEx.DeleteIfExists(DirPath);
}

@ -1,26 +1,18 @@
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using FluentAssertions;
using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class CsvContentSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public CsvContentSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Messages_are_exported_correctly()
{
// Act
var document = await _exportWrapper.ExportAsCsvAsync(ChannelIds.DateRangeTestCases);
var document = await ExportWrapper.ExportAsCsvAsync(ChannelIds.DateRangeTestCases);
// Assert
document.Should().ContainAll(

@ -4,9 +4,9 @@ using System.Linq;
using System.Threading.Tasks;
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;
@ -15,22 +15,14 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class DateRangeSpecs : IClassFixture<TempOutputFixture>
public class DateRangeSpecs
{
private readonly TempOutputFixture _tempOutput;
public DateRangeSpecs(TempOutputFixture tempOutput)
{
_tempOutput = tempOutput;
}
[Fact]
public async Task Messages_filtered_after_specific_date_only_include_messages_sent_after_that_date()
{
// Arrange
var after = new DateTimeOffset(2021, 07, 24, 0, 0, 0, TimeSpan.Zero);
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -38,13 +30,13 @@ public class DateRangeSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.DateRangeTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
After = Snowflake.FromDate(after)
}.ExecuteAsync(new FakeConsole());
// Assert
var timestamps = Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("timestamp").GetDateTimeOffset())
@ -74,7 +66,7 @@ public class DateRangeSpecs : IClassFixture<TempOutputFixture>
{
// Arrange
var before = new DateTimeOffset(2021, 07, 24, 0, 0, 0, TimeSpan.Zero);
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -82,13 +74,13 @@ public class DateRangeSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.DateRangeTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
Before = Snowflake.FromDate(before)
}.ExecuteAsync(new FakeConsole());
// Assert
var timestamps = Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("timestamp").GetDateTimeOffset())
@ -117,7 +109,7 @@ public class DateRangeSpecs : IClassFixture<TempOutputFixture>
// Arrange
var after = new DateTimeOffset(2021, 07, 24, 0, 0, 0, TimeSpan.Zero);
var before = new DateTimeOffset(2021, 08, 01, 0, 0, 0, TimeSpan.Zero);
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -125,14 +117,14 @@ public class DateRangeSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.DateRangeTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
Before = Snowflake.FromDate(before),
After = Snowflake.FromDate(after)
}.ExecuteAsync(new FakeConsole());
// Assert
var timestamps = Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("timestamp").GetDateTimeOffset())

@ -3,9 +3,9 @@ using System.Linq;
using System.Threading.Tasks;
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.Exporting;
using DiscordChatExporter.Core.Exporting.Filtering;
using FluentAssertions;
@ -14,21 +14,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class FilterSpecs : IClassFixture<TempOutputFixture>
public class FilterSpecs
{
private readonly TempOutputFixture _tempOutput;
public FilterSpecs(TempOutputFixture tempOutput)
{
_tempOutput = tempOutput;
}
[Fact]
public async Task Messages_filtered_by_text_only_include_messages_that_contain_that_text()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -36,13 +28,13 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.FilterTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("some text")
}.ExecuteAsync(new FakeConsole());
// Assert
Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
@ -54,7 +46,7 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
public async Task Messages_filtered_by_author_only_include_messages_sent_by_that_author()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -62,13 +54,13 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.FilterTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("from:Tyrrrz")
}.ExecuteAsync(new FakeConsole());
// Assert
Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("author").GetProperty("name").GetString())
@ -80,7 +72,7 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
public async Task Messages_filtered_by_content_only_include_messages_that_have_that_content()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -88,13 +80,13 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.FilterTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("has:image")
}.ExecuteAsync(new FakeConsole());
// Assert
Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
@ -106,7 +98,7 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
public async Task Messages_filtered_by_pin_only_include_messages_that_have_been_pinned()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -114,13 +106,13 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.FilterTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("has:pin")
}.ExecuteAsync(new FakeConsole());
// Assert
Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
@ -132,7 +124,7 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
public async Task Messages_filtered_by_mention_only_include_messages_that_have_that_mention()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -140,13 +132,13 @@ public class FilterSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.FilterTestCases },
ExportFormat = ExportFormat.Json,
OutputPath = filePath,
OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("mentions:Tyrrrz")
}.ExecuteAsync(new FakeConsole());
// Assert
Json
.Parse(await File.ReadAllTextAsync(filePath))
.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())

@ -1,7 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Dom;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -9,21 +9,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlAttachmentSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public HtmlAttachmentSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Message_with_a_generic_attachment_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885587844989612074")
);
@ -48,7 +40,7 @@ public class HtmlAttachmentSpecs
public async Task Message_with_an_image_attachment_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885654862656843786")
);
@ -71,7 +63,7 @@ public class HtmlAttachmentSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/333
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885655761919836171")
);
@ -91,7 +83,7 @@ public class HtmlAttachmentSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/333
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885656175620808734")
);

@ -1,7 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Dom;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -9,21 +9,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlContentSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public HtmlContentSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Messages_are_exported_correctly()
{
// Act
var messages = await _exportWrapper.GetMessagesAsHtmlAsync(ChannelIds.DateRangeTestCases);
var messages = await ExportWrapper.GetMessagesAsHtmlAsync(ChannelIds.DateRangeTestCases);
// Assert
messages.Select(e => e.GetAttribute("data-message-id")).Should().Equal(
@ -55,7 +47,7 @@ public class HtmlContentSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/633
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.ReplyTestCases,
Snowflake.Parse("1072165330853576876")
);

@ -1,7 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Dom;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -9,21 +9,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlEmbedSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public HtmlEmbedSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Message_with_an_embed_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("866769910729146400")
);
@ -46,7 +38,7 @@ public class HtmlEmbedSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/537
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("991768701126852638")
);
@ -66,7 +58,7 @@ public class HtmlEmbedSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/682
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("991768701126852638")
);
@ -80,7 +72,7 @@ public class HtmlEmbedSpecs
public async Task Message_containing_a_gifv_link_is_rendered_with_a_video_embed()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("1019234520349814794")
);
@ -98,7 +90,7 @@ public class HtmlEmbedSpecs
public async Task Message_containing_a_gifv_link_and_nothing_else_is_rendered_without_text_content()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("1019234520349814794")
);
@ -114,7 +106,7 @@ public class HtmlEmbedSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/657
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("867886632203976775")
);
@ -130,7 +122,7 @@ public class HtmlEmbedSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/570
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("866472508588294165")
);
@ -146,7 +138,7 @@ public class HtmlEmbedSpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/695
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("991757444017557665")
);

@ -4,32 +4,24 @@ 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.Exporting;
using FluentAssertions;
using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlGroupingSpecs : IClassFixture<TempOutputFixture>
public class HtmlGroupingSpecs
{
private readonly TempOutputFixture _tempOutput;
public HtmlGroupingSpecs(TempOutputFixture tempOutput)
{
_tempOutput = tempOutput;
}
[Fact]
public async Task Messages_are_grouped_correctly()
{
// https://github.com/Tyrrrz/DiscordChatExporter/issues/152
// Arrange
var filePath = _tempOutput.GetTempFilePath();
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
@ -37,12 +29,12 @@ public class HtmlGroupingSpecs : IClassFixture<TempOutputFixture>
Token = Secrets.DiscordToken,
ChannelIds = new[] { ChannelIds.GroupingTestCases },
ExportFormat = ExportFormat.HtmlDark,
OutputPath = filePath
OutputPath = file.Path
}.ExecuteAsync(new FakeConsole());
// Assert
var messageGroups = Utils.Html
.Parse(await File.ReadAllTextAsync(filePath))
var messageGroups = Html
.Parse(await File.ReadAllTextAsync(file.Path))
.QuerySelectorAll(".chatlog__message-group");
messageGroups.Should().HaveCount(2);

@ -1,6 +1,6 @@
using System.Threading.Tasks;
using AngleSharp.Dom;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -8,21 +8,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlMentionSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public HtmlMentionSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task User_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866458840245076028")
);
@ -36,7 +28,7 @@ public class HtmlMentionSpecs
public async Task Text_channel_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866459040480624680")
);
@ -49,7 +41,7 @@ public class HtmlMentionSpecs
public async Task Voice_channel_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866459175462633503")
);
@ -62,7 +54,7 @@ public class HtmlMentionSpecs
public async Task Role_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866459254693429258")
);

@ -1,6 +1,6 @@
using System.Threading.Tasks;
using AngleSharp.Dom;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -8,21 +8,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlReplySpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public HtmlReplySpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Reply_to_a_normal_message_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.ReplyTestCases,
Snowflake.Parse("866460738239725598")
);
@ -38,7 +30,7 @@ public class HtmlReplySpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/645
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.ReplyTestCases,
Snowflake.Parse("866460975388819486")
);
@ -56,7 +48,7 @@ public class HtmlReplySpecs
// https://github.com/Tyrrrz/DiscordChatExporter/issues/634
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.ReplyTestCases,
Snowflake.Parse("866462470335627294")
);

@ -1,5 +1,5 @@
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -7,21 +7,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class HtmlStickerSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public HtmlStickerSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Message_with_a_PNG_based_sticker_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.StickerTestCases,
Snowflake.Parse("939670623158943754")
);
@ -35,7 +27,7 @@ public class HtmlStickerSpecs
public async Task Message_with_a_Lottie_based_sticker_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsHtmlAsync(
var message = await ExportWrapper.GetMessageAsHtmlAsync(
ChannelIds.StickerTestCases,
Snowflake.Parse("939670526517997590")
);

@ -1,6 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -8,21 +8,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class JsonAttachmentSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public JsonAttachmentSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Message_with_a_generic_attachment_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885587844989612074")
);
@ -43,7 +35,7 @@ public class JsonAttachmentSpecs
public async Task Message_with_an_image_attachment_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885654862656843786")
);
@ -64,7 +56,7 @@ public class JsonAttachmentSpecs
public async Task Message_with_a_video_attachment_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885655761919836171")
);
@ -85,7 +77,7 @@ public class JsonAttachmentSpecs
public async Task Message_with_an_audio_attachment_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.AttachmentTestCases,
Snowflake.Parse("885656175620808734")
);

@ -1,27 +1,19 @@
using System.Linq;
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using FluentAssertions;
using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class JsonContentSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public JsonContentSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Messages_are_exported_correctly()
{
// Act
var messages = await _exportWrapper.GetMessagesAsJsonAsync(ChannelIds.DateRangeTestCases);
var messages = await ExportWrapper.GetMessagesAsJsonAsync(ChannelIds.DateRangeTestCases);
// Assert
messages.Select(j => j.GetProperty("id").GetString()).Should().Equal(

@ -1,6 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -8,21 +8,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class JsonEmbedSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public JsonEmbedSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Message_with_an_embed_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.EmbedTestCases,
Snowflake.Parse("866769910729146400")
);

@ -1,6 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -8,21 +8,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class JsonMentionSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public JsonMentionSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task User_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866458840245076028")
);
@ -42,7 +34,7 @@ public class JsonMentionSpecs
public async Task Text_channel_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866459040480624680")
);
@ -55,7 +47,7 @@ public class JsonMentionSpecs
public async Task Voice_channel_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866459175462633503")
);
@ -68,7 +60,7 @@ public class JsonMentionSpecs
public async Task Role_mention_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.MentionTestCases,
Snowflake.Parse("866459254693429258")
);

@ -1,6 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using DiscordChatExporter.Core.Discord;
using FluentAssertions;
@ -8,21 +8,13 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class JsonStickerSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public JsonStickerSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Message_with_a_PNG_based_sticker_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.StickerTestCases,
Snowflake.Parse("939670623158943754")
);
@ -43,7 +35,7 @@ public class JsonStickerSpecs
public async Task Message_with_a_Lottie_based_sticker_is_rendered_correctly()
{
// Act
var message = await _exportWrapper.GetMessageAsJsonAsync(
var message = await ExportWrapper.GetMessageAsJsonAsync(
ChannelIds.StickerTestCases,
Snowflake.Parse("939670526517997590")
);

@ -2,9 +2,9 @@
using System.Threading.Tasks;
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.Exporting;
using DiscordChatExporter.Core.Exporting.Partitioning;
using FluentAssertions;
@ -12,23 +12,14 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class PartitioningSpecs : IClassFixture<TempOutputFixture>
public class PartitioningSpecs
{
private readonly TempOutputFixture _tempOutput;
public PartitioningSpecs(TempOutputFixture tempOutput)
{
_tempOutput = tempOutput;
}
[Fact]
public async Task Messages_partitioned_by_count_are_split_into_a_corresponding_number_of_files()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath);
var dirPath = Path.GetDirectoryName(filePath) ?? Directory.GetCurrentDirectory();
using var dir = TempDir.Create();
var filePath = Path.Combine(dir.Path, "output.html");
// Act
await new ExportChannelsCommand
@ -41,7 +32,7 @@ public class PartitioningSpecs : IClassFixture<TempOutputFixture>
}.ExecuteAsync(new FakeConsole());
// Assert
Directory.EnumerateFiles(dirPath, fileNameWithoutExt + "*")
Directory.EnumerateFiles(dir.Path, "output*")
.Should()
.HaveCount(3);
}
@ -50,9 +41,8 @@ public class PartitioningSpecs : IClassFixture<TempOutputFixture>
public async Task Messages_partitioned_by_file_size_are_split_into_a_corresponding_number_of_files()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath);
var dirPath = Path.GetDirectoryName(filePath) ?? Directory.GetCurrentDirectory();
using var dir = TempDir.Create();
var filePath = Path.Combine(dir.Path, "output.html");
// Act
await new ExportChannelsCommand
@ -65,7 +55,7 @@ public class PartitioningSpecs : IClassFixture<TempOutputFixture>
}.ExecuteAsync(new FakeConsole());
// Assert
Directory.EnumerateFiles(dirPath, fileNameWithoutExt + "*")
Directory.EnumerateFiles(dir.Path, "output*")
.Should()
.HaveCount(8);
}

@ -1,26 +1,18 @@
using System.Threading.Tasks;
using DiscordChatExporter.Cli.Tests.Fixtures;
using DiscordChatExporter.Cli.Tests.Infra;
using DiscordChatExporter.Cli.Tests.TestData;
using FluentAssertions;
using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class PlainTextContentSpecs
{
private readonly ExportWrapperFixture _exportWrapper;
public PlainTextContentSpecs(ExportWrapperFixture exportWrapper)
{
_exportWrapper = exportWrapper;
}
[Fact]
public async Task Messages_are_exported_correctly()
{
// Act
var document = await _exportWrapper.ExportAsPlainTextAsync(ChannelIds.DateRangeTestCases);
var document = await ExportWrapper.ExportAsPlainTextAsync(ChannelIds.DateRangeTestCases);
// Assert
document.Should().ContainAll(

@ -3,7 +3,6 @@ using System.Linq;
using System.Threading.Tasks;
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;
@ -13,22 +12,14 @@ using Xunit;
namespace DiscordChatExporter.Cli.Tests.Specs;
[Collection(nameof(ExportWrapperCollection))]
public class SelfContainedSpecs : IClassFixture<TempOutputFixture>
public class SelfContainedSpecs
{
private readonly TempOutputFixture _tempOutput;
public SelfContainedSpecs(TempOutputFixture tempOutput)
{
_tempOutput = tempOutput;
}
[Fact]
public async Task Messages_in_self_contained_export_only_reference_local_file_resources()
{
// Arrange
var filePath = _tempOutput.GetTempFilePath();
var dirPath = Path.GetDirectoryName(filePath) ?? Directory.GetCurrentDirectory();
using var dir = TempDir.Create();
var filePath = Path.Combine(dir.Path, "output.html");
// Act
await new ExportChannelsCommand
@ -45,7 +36,7 @@ public class SelfContainedSpecs : IClassFixture<TempOutputFixture>
.Parse(await File.ReadAllTextAsync(filePath))
.QuerySelectorAll("body [src]")
.Select(e => e.GetAttribute("src")!)
.Select(f => Path.GetFullPath(f, dirPath))
.Select(f => Path.GetFullPath(f, dir.Path))
.All(File.Exists)
.Should()
.BeTrue();

@ -1,23 +0,0 @@
using System.IO;
namespace DiscordChatExporter.Cli.Tests.Utils;
internal static class DirectoryEx
{
public static void DeleteIfExists(string dirPath, bool recursive = true)
{
try
{
Directory.Delete(dirPath, recursive);
}
catch (DirectoryNotFoundException)
{
}
}
public static void Reset(string dirPath)
{
DeleteIfExists(dirPath);
Directory.CreateDirectory(dirPath);
}
}

@ -0,0 +1,41 @@
using System;
using System.IO;
using System.Reflection;
using PathEx = System.IO.Path;
namespace DiscordChatExporter.Cli.Tests.Utils;
internal partial class TempDir : IDisposable
{
public string Path { get; }
public TempDir(string path) =>
Path = path;
public void Dispose()
{
try
{
Directory.Delete(Path, true);
}
catch (DirectoryNotFoundException)
{
}
}
}
internal partial class TempDir
{
public static TempDir Create()
{
var dirPath = PathEx.Combine(
PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory(),
"Temp",
Guid.NewGuid().ToString()
);
Directory.CreateDirectory(dirPath);
return new TempDir(dirPath);
}
}

@ -0,0 +1,45 @@
using System;
using System.IO;
using System.Reflection;
using PathEx = System.IO.Path;
namespace DiscordChatExporter.Cli.Tests.Utils;
internal partial class TempFile : IDisposable
{
public string Path { get; }
public TempFile(string path) =>
Path = path;
public void Dispose()
{
try
{
File.Delete(Path);
}
catch (FileNotFoundException)
{
}
}
}
internal partial class TempFile
{
public static TempFile Create()
{
var dirPath = PathEx.Combine(
PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory(),
"Temp"
);
Directory.CreateDirectory(dirPath);
var filePath = PathEx.Combine(
dirPath,
Guid.NewGuid() + ".tmp"
);
return new TempFile(filePath);
}
}

@ -1,6 +1,5 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"methodDisplayOptions": "all",
"methodDisplay": "method",
"parallelizeTestCollections": true
"methodDisplay": "method"
}

@ -50,7 +50,7 @@ public class ChannelExporter
{
cancellationToken.ThrowIfCancellationRequested();
// Skips any messages that fail to pass the supplied filter
// Skip messages that fail to pass the supplied filter
if (!request.MessageFilter.IsMatch(message))
continue;

@ -78,13 +78,11 @@ internal partial class ExportAssetDownloader
internal partial class ExportAssetDownloader
{
private static string GetUrlHash(string url)
{
using var hash = SHA256.Create();
var data = hash.ComputeHash(Encoding.UTF8.GetBytes(url));
return data.ToHex().Truncate(5); // 5 chars ought to be enough for anybody
}
private static string GetUrlHash(string url) => SHA256
.HashData(Encoding.UTF8.GetBytes(url))
.ToHex()
// 5 chars ought to be enough for anybody
.Truncate(5);
private static string GetFileNameFromUrl(string url)
{

@ -26,7 +26,7 @@ internal partial class MessageExporter : IAsyncDisposable
{
await _writer.WritePostambleAsync(cancellationToken);
}
// Writer must be disposed, even if writing postamble fails
// Writer must be disposed, even if it fails to write the postamble
finally
{
await _writer.DisposeAsync();
@ -45,16 +45,13 @@ internal partial class MessageExporter : IAsyncDisposable
_partitionIndex++;
}
// Writer is still valid - return
// Writer is still valid, return
if (_writer is not null)
return _writer;
Directory.CreateDirectory(_context.Request.OutputBaseDirPath);
var filePath = GetPartitionFilePath(_context.Request.OutputBaseFilePath, _partitionIndex);
var dirPath = Path.GetDirectoryName(_context.Request.OutputBaseFilePath);
if (!string.IsNullOrWhiteSpace(dirPath))
Directory.CreateDirectory(dirPath);
var writer = CreateMessageWriter(filePath, _context.Request.Format, _context);
await writer.WritePreambleAsync(cancellationToken);
@ -74,7 +71,7 @@ internal partial class MessageExporter
{
private static string GetPartitionFilePath(string baseFilePath, int partitionIndex)
{
// First partition - don't change file name
// First partition, don't change file name
if (partitionIndex <= 0)
return baseFilePath;
@ -92,19 +89,14 @@ internal partial class MessageExporter
private static MessageWriter CreateMessageWriter(
string filePath,
ExportFormat format,
ExportContext context)
{
// Stream will be disposed by the underlying writer
var stream = File.Create(filePath);
return format switch
ExportContext context) =>
format switch
{
ExportFormat.PlainText => new PlainTextMessageWriter(stream, context),
ExportFormat.Csv => new CsvMessageWriter(stream, context),
ExportFormat.HtmlDark => new HtmlMessageWriter(stream, context, "Dark"),
ExportFormat.HtmlLight => new HtmlMessageWriter(stream, context, "Light"),
ExportFormat.Json => new JsonMessageWriter(stream, context),
ExportFormat.PlainText => new PlainTextMessageWriter(File.Create(filePath), context),
ExportFormat.Csv => new CsvMessageWriter(File.Create(filePath), context),
ExportFormat.HtmlDark => new HtmlMessageWriter(File.Create(filePath), context, "Dark"),
ExportFormat.HtmlLight => new HtmlMessageWriter(File.Create(filePath), context, "Light"),
ExportFormat.Json => new JsonMessageWriter(File.Create(filePath), context),
_ => throw new ArgumentOutOfRangeException(nameof(format), $"Unknown export format '{format}'.")
};
}
}

@ -6,12 +6,10 @@ public static class BinaryExtensions
{
public static string ToHex(this byte[] data)
{
var buffer = new StringBuilder();
var buffer = new StringBuilder(2 * data.Length);
foreach (var t in data)
{
buffer.Append(t.ToString("X2"));
}
return buffer.ToString();
}

Loading…
Cancel
Save