T1-4/T1-7: search engine resolver + site zoom store (TDD RED->GREEN, 32 tests)

This commit is contained in:
Yun Chan 2026-08-15 12:04:23 +09:00
parent 21b7c4d8bd
commit 470c9267f2
9 changed files with 522 additions and 0 deletions

View file

@ -0,0 +1,72 @@
using System.Windows.Threading;
namespace VideoDownloader.Tests.Infrastructure;
/// <summary>
/// WPF/WebView2 E2E 테스트용 STA 디스패처. 전용 STA 스레드에서 Dispatcher.Run() 으로
/// 메시지 펌프를 돌리며, 테스트 코드는 InvokeAsync 로 UI 스레드에 작업을 올린다.
/// WebView2 이벤트(비동기)는 UI 스레드 디스패처에서 발생하므로 async 흐름도 이 펌프 위에서 완결된다.
/// </summary>
public sealed class StaDispatcher : IDisposable
{
private readonly Thread _thread;
private readonly TaskCompletionSource _ready = new(TaskCreationOptions.RunContinuationsAsynchronously);
private Dispatcher? _dispatcher;
private StaDispatcher(Thread thread)
{
_thread = thread;
}
public Dispatcher Dispatcher =>
_dispatcher ?? throw new InvalidOperationException("STA dispatcher not started");
public static StaDispatcher Start()
{
var sta = default(StaDispatcher)!;
var thread = new Thread(() =>
{
sta._dispatcher = Dispatcher.CurrentDispatcher;
sta._ready.TrySetResult();
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
sta = new StaDispatcher(thread);
thread.Start();
sta._ready.Task.Wait(5000);
return sta;
}
public Task<T> InvokeAsync<T>(Func<Task<T>> func)
=> Dispatcher.InvokeAsync(func).Task.Unwrap();
public Task InvokeAsync(Func<Task> func)
=> Dispatcher.InvokeAsync(func).Task.Unwrap();
public T Invoke<T>(Func<T> func) => Dispatcher.Invoke(func);
public void Invoke(Action action) => Dispatcher.Invoke(action);
public void Dispose()
{
try { Dispatcher.InvokeShutdown(); } catch { }
try { _thread.Join(5000); } catch { }
}
}
/// <summary>WebView2 비동기 초기화/이벤트 대기 공통 유틸.</summary>
public static class Wait
{
public static async Task<T> Until<T>(Func<Task<T>> poll, Func<T, bool> done, int timeoutMs = 15000, int intervalMs = 100)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.ElapsedMilliseconds < timeoutMs)
{
var v = await poll();
if (done(v)) return v;
await Task.Delay(intervalMs);
}
throw new TimeoutException($"조건 미충족 ({timeoutMs}ms 초과)");
}
}