1
0
Fork 0

fix(converters): fix HtmlProvider WebView2 thread crash and add MarkdownProvider for pure .NET conversions

This commit is contained in:
Yun Chan 2026-09-08 04:16:48 +09:00
parent 63d50c165d
commit 4e5285e27c
7 changed files with 447 additions and 28 deletions

View file

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Everything2Everything.Core;
using Everything2Everything.Core.Converters;
using Everything2Everything.Core.Providers;
using Xunit;
using Xunit.Abstractions;
namespace Everything2Everything.Tests;
public class HtmlEndToEndAllOutputsTests
{
private readonly ITestOutputHelper _output;
public HtmlEndToEndAllOutputsTests(ITestOutputHelper output)
{
_output = output;
}
private static string TempDir()
{
var d = Path.Combine(Path.GetTempPath(), "e2e_html_all_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(d);
return d;
}
[Theory]
[InlineData(".pdf")]
[InlineData(".png")]
[InlineData(".jpg")]
[InlineData(".webp")]
[InlineData(".avif")]
[InlineData(".bmp")]
[InlineData(".tiff")]
[InlineData(".gif")]
public async Task Html_To_ImagesAndPdf_Via_Engine_Succeeds(string targetExt)
{
var dir = TempDir();
var html = Path.Combine(dir, "sample.html");
await File.WriteAllTextAsync(html, "<html><body><h1 style='color:red;'>Test Page</h1><p>Paragraph content</p></body></html>");
var engine = Everything2EverythingBootstrap.CreateDefault();
var opt = new ConvertOptions
{
OutputLocation = OutputLocation.Custom,
CustomOutputDirectory = dir
};
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var result = await engine.ConvertOneAsync(html, targetExt, opt, null, cts.Token);
_output.WriteLine($"Convert .html -> {targetExt}: Status={result.Status}, Msg={result.Message}");
Assert.Equal(ConvertStatus.Success, result.Status);
Assert.NotEmpty(result.OutputPaths);
Assert.True(File.Exists(result.OutputPaths[0]));
Assert.True(new FileInfo(result.OutputPaths[0]).Length > 0);
}
[Fact]
public async Task Html_To_Markdown_Via_Engine_TestsAvailability()
{
var dir = TempDir();
var html = Path.Combine(dir, "sample.html");
await File.WriteAllTextAsync(html, "<html><body><h1>Markdown Title</h1><p>Bold <b>text</b></p></body></html>");
var engine = Everything2EverythingBootstrap.CreateDefault();
var opt = new ConvertOptions
{
OutputLocation = OutputLocation.Custom,
CustomOutputDirectory = dir
};
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var result = await engine.ConvertOneAsync(html, ".md", opt, null, cts.Token);
_output.WriteLine($"Convert .html -> .md: Status={result.Status}, Msg={result.Message}");
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Everything2Everything.Core;
using Everything2Everything.Core.Converters;
using Everything2Everything.Core.Providers;
using Xunit;
namespace Everything2Everything.Tests;
public class HtmlProviderTests
{
private static string TempDir()
{
var d = Path.Combine(Path.GetTempPath(), "e2e_html_test_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(d);
return d;
}
[Fact]
public async Task HtmlProvider_ConvertAsync_HtmlToPdf_Succeeds()
{
var dir = TempDir();
var html = Path.Combine(dir, "test.html");
await File.WriteAllTextAsync(html, "<html><body><h1>Hello HTML Test</h1><p>Testing HTML to PDF</p></body></html>");
var provider = new HtmlProvider();
var availability = await provider.CheckAvailabilityAsync();
if (!availability.IsReady) return;
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var result = await provider.ConvertAsync(html, dir, ".pdf", new ConvertOptions(), null, cts.Token);
Assert.Equal(ConvertStatus.Success, result.Status);
Assert.NotEmpty(result.OutputPaths);
Assert.True(File.Exists(result.OutputPaths[0]));
Assert.True(new FileInfo(result.OutputPaths[0]).Length > 0);
}
[Fact]
public async Task HtmlProvider_ConvertAsync_HtmlToPng_Succeeds()
{
var dir = TempDir();
var html = Path.Combine(dir, "test.html");
await File.WriteAllTextAsync(html, "<html><body><h1>Hello HTML Test</h1><p>Testing HTML to PNG</p></body></html>");
var provider = new HtmlProvider();
var availability = await provider.CheckAvailabilityAsync();
if (!availability.IsReady) return;
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var result = await provider.ConvertAsync(html, dir, ".png", new ConvertOptions(), null, cts.Token);
Assert.Equal(ConvertStatus.Success, result.Status);
Assert.NotEmpty(result.OutputPaths);
Assert.True(File.Exists(result.OutputPaths[0]));
Assert.True(new FileInfo(result.OutputPaths[0]).Length > 0);
}
}

View file

@ -0,0 +1,79 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Everything2Everything.Core;
using Everything2Everything.Core.Converters;
using Everything2Everything.Core.Providers;
using Xunit;
namespace Everything2Everything.Tests;
public class MarkdownProviderTests
{
private static string TempDir()
{
var d = Path.Combine(Path.GetTempPath(), "e2e_md_test_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(d);
return d;
}
[Fact]
public async Task MarkdownProvider_HtmlToMd_ConvertsProperly()
{
var dir = TempDir();
var html = Path.Combine(dir, "input.html");
await File.WriteAllTextAsync(html, "<h1>Title Here</h1><p>Paragraph with <strong>bold</strong> text</p>");
var provider = new MarkdownProvider();
var res = await provider.ConvertAsync(html, dir, ".md", new ConvertOptions(), null, CancellationToken.None);
Assert.Equal(ConvertStatus.Success, res.Status);
var content = await File.ReadAllTextAsync(res.OutputPaths[0]);
Assert.Contains("# Title Here", content);
Assert.Contains("**bold**", content);
}
[Fact]
public async Task MarkdownProvider_MdToHtml_ConvertsProperly()
{
var dir = TempDir();
var md = Path.Combine(dir, "input.md");
await File.WriteAllTextAsync(md, "# Hello Markdown\n\nThis is a *test*.");
var provider = new MarkdownProvider();
var res = await provider.ConvertAsync(md, dir, ".html", new ConvertOptions(), null, CancellationToken.None);
Assert.Equal(ConvertStatus.Success, res.Status);
var content = await File.ReadAllTextAsync(res.OutputPaths[0]);
Assert.Contains("Hello Markdown</h1>", content);
Assert.Contains("<em>test</em>", content);
}
[Fact]
public async Task MarkdownProvider_Availability_IsAlwaysReady()
{
var provider = new MarkdownProvider();
var avail = await provider.CheckAvailabilityAsync();
Assert.True(avail.IsReady);
Assert.Equal(ProviderStatus.Available, provider.Capability.Status);
}
[Fact]
public async Task Engine_HtmlToMd_RoutesToMarkdownProvider()
{
var dir = TempDir();
var html = Path.Combine(dir, "input.html");
await File.WriteAllTextAsync(html, "<h2>Section</h2><p>Content</p>");
var engine = Everything2EverythingBootstrap.CreateDefault();
var path = engine.Providers.Graph.FindBestPath(".html", ".md");
Assert.NotNull(path);
Assert.Single(path);
Assert.IsType<MarkdownProvider>(path![0].Provider);
var opt = new ConvertOptions { OutputLocation = OutputLocation.Custom, CustomOutputDirectory = dir };
var res = await engine.ConvertOneAsync(html, ".md", opt);
Assert.Equal(ConvertStatus.Success, res.Status);
}
}

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Everything2Everything.Core;
using Everything2Everything.Core.Providers;
using Xunit;
using Xunit.Abstractions;
namespace Everything2Everything.Tests;
public class MatrixIntegrityTests
{
private readonly ITestOutputHelper _output;
public MatrixIntegrityTests(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void Audit_AllRegisteredProviders_AndPrintMatrix()
{
var engine = Everything2EverythingBootstrap.CreateDefault();
var providers = engine.Providers.All.ToList();
_output.WriteLine($"=== Total Registered Providers: {providers.Count} ===");
foreach (var p in providers)
{
var cap = p.Capability;
_output.WriteLine($"Provider [{cap.Id}] {cap.DisplayName} | Status: {cap.Status} | Pairs: {cap.SupportedConversions.Count}");
var missingDeps = cap.ExternalDependencies.Where(d => d.IsRequired).Select(d => d.Name).ToList();
if (missingDeps.Any())
{
_output.WriteLine($" Required deps: {string.Join(", ", missingDeps)}");
}
}
Assert.NotEmpty(providers);
}
[Fact]
public void Audit_HtmlReachability_InGraph()
{
var engine = Everything2EverythingBootstrap.CreateDefault();
var outputs = engine.Providers.OutputsForInput(".html");
_output.WriteLine($"HTML Reachable Outputs ({outputs.Count}): {string.Join(", ", outputs)}");
Assert.Contains(".pdf", outputs);
Assert.Contains(".png", outputs);
Assert.Contains(".jpg", outputs);
Assert.Contains(".webp", outputs);
Assert.Contains(".md", outputs);
}
}