Phase A: tab architecture integrated into MainWindow - tab strip UI, per-tab WebView2 lifecycle, new-window->tab w/ deferral, audio indicator/mute, session save/restore, shortcuts; E2E on real window (56 tests GREEN)

This commit is contained in:
Yun Chan 2026-08-15 14:48:17 +09:00
parent be01a116ca
commit b6b7fc341b
7 changed files with 389 additions and 106 deletions

View file

@ -5,14 +5,14 @@ using VideoDownloader.Tests.Infrastructure;
namespace VideoDownloader.Tests.E2E;
/// <summary>
/// E2E 스모크: 실제 MainWindow 를 기동해 WebView2 초기화·탐색·검색 바·전체화면 전환이
/// 크래시 없이 동작하는지 검증한다(Phase 0 통합 확인). 네트워크 의존 제거를 위해 about:blank 사용.
/// E2E (Phase A): 실제 MainWindow 기동 → WebView2 초기화 → about:blank 탐색 →
/// 다중 탭(생성/전환/닫기/Ctrl+Shift+T 복원) + 검색 바 + 전체화면까지 실 창에서 검증.
/// </summary>
[Collection("E2E")]
public class MainWindowSmokeE2ETests
{
[Fact]
public async Task MainWindow_기동_탐색_검색바_전체화면_스모크()
public async Task MainWindow_기동_탐색_탭전환_닫기_복원_검색바_전체화면_E2E()
{
using var sta = StaDispatcher.Start();
WpfTestApp.Ensure(sta.Dispatcher);
@ -21,38 +21,70 @@ public class MainWindowSmokeE2ETests
var window = new MainWindow();
window.Show();
// WebView2 초기화 대기 (Loaded → InitializeBrowserAsync)
var cv = await Wait.Until(
() => Task.FromResult(window.Browser.CoreWebView2),
c => c != null, 30000);
// 첫 탭 WebView2 초기화 대기
var first = await Wait.Until(
() => Task.FromResult(window.Tabs.ActiveTab),
t => t?.Core != null, 30000);
var cv1 = first.Core!;
// about:blank 탐색 (네트워크 불필요)
window.NavigateTo("about:blank");
await Wait.Until(
() => Task.FromResult(cv.Source ?? ""),
s => s.Contains("about:blank"), 15000);
await Wait.Until(() => Task.FromResult(cv1.Source ?? ""), s => s.Contains("about:blank"), 15000);
// T1-3 검색 바 열기/닫기
// ── Phase A: 다중 탭 ──
var tab2 = window.Tabs.NewTab();
var cv2 = await Wait.Until(() => Task.FromResult(tab2.Core), c => c != null, 30000);
cv2.NavigateToString("<html><body><p id='x'>hello-tab2</p></body></html>");
await WaitUntilNavCompleted(cv2);
Assert.NotSame(first, window.Tabs.ActiveTab); // NewTab 이 활성화
Assert.Equal(2, window.Tabs.Tabs.Count);
// 탭 전환: 첫 탭으로
window.Tabs.Activate(first);
var switched = ReferenceEquals(window.Tabs.ActiveTab, first);
// 탭 닫기 → 오른쪽 이웃 활성화 → Ctrl+Shift+T 복원
window.Tabs.CloseTab(tab2);
var countAfterClose = window.Tabs.Tabs.Count;
var reopened = window.Tabs.ReopenClosedTab();
var countAfterReopen = window.Tabs.Tabs.Count;
// ── T1-3 검색 바 ──
window.OpenFind();
var findVisible = window.FindBar.Visibility == Visibility.Visible;
window.CloseFind();
var findCollapsed = window.FindBar.Visibility == Visibility.Collapsed;
// T1-11 전체화면 토글
// ── T1-11 전체화면 ──
window.ToggleFullscreen();
var toolbarHidden = window.ToolbarRow.Visibility == Visibility.Collapsed;
var toolbarHidden = window.ToolbarRow.Visibility == Visibility.Collapsed
&& window.TabStripRow.Visibility == Visibility.Collapsed;
window.ToggleFullscreen();
var toolbarBack = window.ToolbarRow.Visibility == Visibility.Visible;
window.Close();
return (init: cv != null, nav: true, findVisible, findCollapsed, toolbarHidden, toolbarBack);
return (init: true, switched, countAfterClose, reopened: reopened != null, countAfterReopen,
findVisible, findCollapsed, toolbarHidden, toolbarBack);
});
Assert.True(ok.init, "WebView2 초기화 실패");
Assert.True(ok.nav);
Assert.True(ok.switched, "탭 전환 실패");
Assert.True(ok.countAfterClose == 1, $"탭 닫기 후 개수 오류: {ok.countAfterClose}");
Assert.True(ok.reopened, "닫은 탭 복원 실패");
Assert.True(ok.countAfterReopen == 2, $"복원 후 개수 오류: {ok.countAfterReopen}");
Assert.True(ok.findVisible, "검색 바 미표시");
Assert.True(ok.findCollapsed, "검색 바 미닫힘");
Assert.True(ok.toolbarHidden, "전체화면에서 툴바 미숨김");
Assert.True(ok.toolbarHidden, "전체화면에서 크롬 미숨김");
Assert.True(ok.toolbarBack, "전체화면 복원 실패");
}
private static Task WaitUntilNavCompleted(Microsoft.Web.WebView2.Core.CoreWebView2 cv)
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs>? h = null;
h = (_, _) => { cv.NavigationCompleted -= h; tcs.TrySetResult(); };
cv.NavigationCompleted += h;
return tcs.Task.WaitAsync(TimeSpan.FromSeconds(15));
}
}