feat(ads/seo/release): v1.2.0 with live Google Ads, isolated ad profile, ads.txt, and SEO/AEO/GEO optimization
This commit is contained in:
parent
a88aafa1e5
commit
0e568f1f0a
975 changed files with 130593 additions and 16783 deletions
511
tests/Paca.Tests/E2E/UiFeatureInteractionsPipelineTests.cs
Normal file
511
tests/Paca.Tests/E2E/UiFeatureInteractionsPipelineTests.cs
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Paca.App;
|
||||
using Paca.App.Browser.Features;
|
||||
using Paca.App.ViewModels;
|
||||
using Paca.Browser;
|
||||
using Paca.Tests.Infrastructure;
|
||||
using Xunit;
|
||||
|
||||
namespace Paca.Tests.E2E;
|
||||
|
||||
/// <summary>
|
||||
/// UI 기능 상호작용 & 웹뷰 컨트롤 E2E 파이프라인 (레이아웃, 퍼미션 트래커, 리더 모드, 설정창 라이브 갱신, 검색창).
|
||||
/// </summary>
|
||||
|
||||
|
||||
[Collection("E2E")]
|
||||
public class LayoutAndInteractionE2ETests
|
||||
{
|
||||
[Fact]
|
||||
public void TabItem_IsPrivate_PropertyChanged_알림_발생()
|
||||
{
|
||||
var tab = new TabItem();
|
||||
string? changedProp = null;
|
||||
tab.PropertyChanged += (s, e) => changedProp = e.PropertyName;
|
||||
|
||||
tab.IsPrivate = true;
|
||||
|
||||
Assert.True(tab.IsPrivate);
|
||||
Assert.Equal(nameof(TabItem.IsPrivate), changedProp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NavProgressTracker_로딩중_카운트_증감_및_리셋()
|
||||
{
|
||||
var tracker = new NavProgressTracker();
|
||||
Assert.False(tracker.IsNavigating);
|
||||
|
||||
tracker.Started();
|
||||
Assert.True(tracker.IsNavigating);
|
||||
|
||||
tracker.Started();
|
||||
Assert.True(tracker.IsNavigating);
|
||||
|
||||
tracker.Completed();
|
||||
Assert.True(tracker.IsNavigating);
|
||||
|
||||
tracker.Completed();
|
||||
Assert.False(tracker.IsNavigating);
|
||||
|
||||
// 초과 완료 호출에도 안전
|
||||
tracker.Completed();
|
||||
Assert.False(tracker.IsNavigating);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CrashReportDialog_생성_시_오류_요약_및_상세_표시()
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
|
||||
sta.Invoke(() =>
|
||||
{
|
||||
var testEx = new InvalidOperationException("테스트용 치명적 오류 메시지", new ArgumentException("내부 예외"));
|
||||
var dlg = new CrashReportDialog(testEx);
|
||||
|
||||
Assert.NotNull(dlg.DetailBox);
|
||||
Assert.Contains("InvalidOperationException", dlg.DetailBox.Text);
|
||||
Assert.Contains("테스트용 치명적 오류 메시지", dlg.DetailBox.Text);
|
||||
Assert.Contains("ArgumentException", dlg.DetailBox.Text);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MainWindow_최대화_및_InPrivate_탭_생성_E2E()
|
||||
{
|
||||
var dataDir = Path.Combine(Path.GetTempPath(), $"vd_e2e_layout_{Guid.NewGuid():N}");
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", dataDir);
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
|
||||
await sta.InvokeAsync(async () =>
|
||||
{
|
||||
var window = new MainWindow();
|
||||
window.Show();
|
||||
|
||||
// 1. 최대화 토글 및 복원 검증
|
||||
Assert.Equal(WindowState.Normal, window.WindowState);
|
||||
window.WindowState = WindowState.Maximized;
|
||||
Assert.Equal(WindowState.Maximized, window.WindowState);
|
||||
|
||||
window.WindowState = WindowState.Normal;
|
||||
Assert.Equal(WindowState.Normal, window.WindowState);
|
||||
|
||||
// 2. InPrivate 탭 생성
|
||||
var privateTab = window.Tabs.NewTab(isPrivate: true);
|
||||
Assert.True(privateTab.IsPrivate, "생성된 탭의 IsPrivate은 true여야 합니다.");
|
||||
|
||||
// 3. 닫기 시 안전 종료
|
||||
window.Close();
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (Directory.Exists(dataDir)) Directory.Delete(dataDir, true); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// E2E (T3-11/T3-14): 실제 WebView2 에서 권한 정책 응답과 추적 요청 차단·집계를 검증.
|
||||
/// TabViewService 와 동일한 배선(PermissionGate/TrackerBlocklist/TrackerCounter)을 사용한다.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class PermissionTrackerE2ETests
|
||||
{
|
||||
[Fact]
|
||||
public async Task 권한_요청이_정책을_거쳐_허용된다_T3_11()
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
var udf = Path.Combine(Path.GetTempPath(), $"vd_e2e_{Guid.NewGuid():N}");
|
||||
var result = await sta.InvokeAsync(async () =>
|
||||
{
|
||||
var window = new Window();
|
||||
var browser = new Microsoft.Web.WebView2.Wpf.WebView2();
|
||||
window.Content = browser;
|
||||
window.Show();
|
||||
|
||||
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: udf);
|
||||
await browser.EnsureCoreWebView2Async(env);
|
||||
var cv = browser.CoreWebView2;
|
||||
|
||||
// TabViewService 배선과 동일: 기억된 판정은 즉시 응답(출처는 이벤트에서 관측된 것으로 기억)
|
||||
var gate = new PermissionGate();
|
||||
cv.PermissionRequested += (_, e) =>
|
||||
{
|
||||
var o = Paca.App.Services.TabViewService.OriginOf(e.Uri);
|
||||
gate.Remember("Notifications", o, PermissionVerdict.Allow); // E2E: 알림은 허용 정책
|
||||
var verdict = gate.Decide(e.PermissionKind.ToString(), o);
|
||||
e.State = verdict == PermissionVerdict.Allow ? CoreWebView2PermissionState.Allow : CoreWebView2PermissionState.Deny;
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var html = "<html><body><script>Notification.requestPermission().then(p => { document.title = 'PERM=' + p; });</script></body></html>";
|
||||
cv.NavigateToString(html);
|
||||
await WaitUntilNavCompleted(cv);
|
||||
var title = await Wait.Until(() => Task.FromResult(cv.DocumentTitle ?? ""),
|
||||
t => t.StartsWith("PERM=", StringComparison.Ordinal), 10000);
|
||||
|
||||
window.Close();
|
||||
return title;
|
||||
});
|
||||
|
||||
try { Directory.Delete(udf, true); } catch { }
|
||||
Assert.Equal("PERM=granted", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 블랙리스트_스크립트가_차단되고_카운터에_집계된다_T3_14()
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
var udf = Path.Combine(Path.GetTempPath(), $"vd_e2e_{Guid.NewGuid():N}");
|
||||
var result = await sta.InvokeAsync(async () =>
|
||||
{
|
||||
var window = new Window();
|
||||
var browser = new Microsoft.Web.WebView2.Wpf.WebView2();
|
||||
window.Content = browser;
|
||||
window.Show();
|
||||
|
||||
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: udf);
|
||||
await browser.EnsureCoreWebView2Async(env);
|
||||
var cv = browser.CoreWebView2;
|
||||
|
||||
var bl = new TrackerBlocklist(new[] { "blocked.test" });
|
||||
var counter = new TrackerCounter();
|
||||
cv.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All);
|
||||
cv.WebResourceRequested += (_, e) =>
|
||||
{
|
||||
if (!bl.Matches(new Uri(e.Request.Uri))) return;
|
||||
e.Response = env.CreateWebResourceResponse(null, 204, "Blocked", "");
|
||||
counter.Record(e.Request.Uri);
|
||||
};
|
||||
|
||||
var html = "<html><body><p>tracker-e2e</p>" +
|
||||
"<script src=\"https://www.blocked.test/a.js\"></script>" +
|
||||
"<script src=\"https://www.blocked.test/b.js\"></script>" +
|
||||
"<script src=\"https://ok.example.com/c.js\"></script>" +
|
||||
"</body></html>";
|
||||
cv.NavigateToString(html);
|
||||
await WaitUntilNavCompleted(cv);
|
||||
await Wait.Until(() => Task.FromResult(counter.Total), t => t >= 2, 10000);
|
||||
|
||||
window.Close();
|
||||
return counter.Total;
|
||||
});
|
||||
|
||||
try { Directory.Delete(udf, true); } catch { }
|
||||
Assert.Equal(2, result); // 정상 도메인(ok.example.com)은 집계되지 않음
|
||||
}
|
||||
|
||||
private static async Task WaitUntilNavCompleted(CoreWebView2 cv)
|
||||
{
|
||||
var tcs = new TaskCompletionSource();
|
||||
EventHandler<CoreWebView2NavigationCompletedEventArgs> h = (_, _) => tcs.TrySetResult();
|
||||
cv.NavigationCompleted += h;
|
||||
if (tcs.Task.IsCompleted) return;
|
||||
await Task.WhenAny(tcs.Task, Task.Delay(15000));
|
||||
cv.NavigationCompleted -= h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// E2E (T2-4): 실제 WebView2 + 번들된 Mozilla Readability.js 로
|
||||
/// 기사 페이지에서 본문을 추출해 리더 뷰로 교체하고 원본 URL 복귀까지 검증.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class ReaderModeE2ETests
|
||||
{
|
||||
private const string ArticleHtml = """
|
||||
<html><head><title>테스트 기사</title></head><body>
|
||||
<nav>광고 광고 광고 내비게이션</nav>
|
||||
<article>
|
||||
<h1>리더 모드 E2E 기사</h1>
|
||||
<p>첫 번째 문단입니다. 이 문단은 Readability 가 본문으로 추출해야 합니다. 충분한 길이의 텍스트가 필요하므로 몇 번 더 반복합니다. 본문 본문 본문.</p>
|
||||
<p>두 번째 문단입니다. 리더 모드는 광고와 내비게이션을 제거하고 본문만 남깁니다. 추출 결과에 이 텍스트가 포함되어야 합니다.</p>
|
||||
<p>세 번째 문단입니다. 문단이 세 개 이상이면 Readability 의 휴리스틱이 본문으로 판정합니다. 마커텍스트-고유문구-레인보우4747.</p>
|
||||
</article>
|
||||
<footer>푸터 링크 모음</footer>
|
||||
</body></html>
|
||||
""";
|
||||
|
||||
[Fact]
|
||||
public async Task 기사를_리더로_추출하고_원본으로_복귀한다()
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
var udf = Path.Combine(Path.GetTempPath(), $"vd_reader_{Guid.NewGuid():N}");
|
||||
var ok = await sta.InvokeAsync(async () =>
|
||||
{
|
||||
var window = new Window();
|
||||
var browser = new Microsoft.Web.WebView2.Wpf.WebView2();
|
||||
window.Content = browser;
|
||||
window.Show();
|
||||
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: udf);
|
||||
await browser.EnsureCoreWebView2Async(env);
|
||||
var cv = browser.CoreWebView2;
|
||||
|
||||
cv.NavigateToString(ArticleHtml);
|
||||
await WaitNav(cv);
|
||||
|
||||
var tab = new TabItem { View = browser };
|
||||
|
||||
// 1) 리더 진입
|
||||
var entered = await ReaderMode.ToggleAsync(tab, lightTheme: false);
|
||||
await Wait.Until(() => Task.FromResult(cv.DocumentTitle ?? ""), t => t.Contains("테스트 기사"), 15000);
|
||||
await Wait.Until(async () => await cv.ExecuteScriptAsync("document.body.innerText"), b => b.Contains("마커텍스트-고유문구-레인보우4747"), 15000);
|
||||
var titleAfter = cv.DocumentTitle ?? "";
|
||||
var bodyText = await cv.ExecuteScriptAsync("document.body.innerText");
|
||||
var hasMarker = bodyText.Contains("마커텍스트-고유문구-레인보우4747");
|
||||
var hasNav = bodyText.Contains("광고 광고 광고");
|
||||
var isReader = tab.IsReader;
|
||||
var originalSaved = tab.ReaderOriginalUrl;
|
||||
|
||||
// 2) 복귀
|
||||
var exited = await ReaderMode.ToggleAsync(tab, lightTheme: false);
|
||||
var restoredReader = tab.IsReader == false;
|
||||
|
||||
window.Close();
|
||||
return (entered: entered == true, titleAfter, hasMarker, navRemoved: !hasNav, isReader, originalSaved, exited: exited == false, restoredReader);
|
||||
});
|
||||
|
||||
try { Directory.Delete(udf, true); } catch { }
|
||||
Assert.True(ok.entered, "리더 진입 실패");
|
||||
Assert.Contains("테스트 기사", ok.titleAfter);
|
||||
Assert.True(ok.hasMarker, "본문 마커 누락");
|
||||
Assert.True(ok.navRemoved, "내비/광고 제거 안됨");
|
||||
Assert.True(ok.isReader);
|
||||
Assert.True(ok.exited);
|
||||
Assert.True(ok.restoredReader);
|
||||
}
|
||||
|
||||
private static Task WaitNav(CoreWebView2 cv)
|
||||
{
|
||||
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
EventHandler<CoreWebView2NavigationCompletedEventArgs>? h = null;
|
||||
h = (_, _) => { cv.NavigationCompleted -= h; tcs.TrySetResult(); };
|
||||
cv.NavigationCompleted += h;
|
||||
return tcs.Task.WaitAsync(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// E2E: 설정창 "앱 업데이트" 섹션(DEPLOYMENT_PLAN §6 자동 업데이트 v1) —
|
||||
/// 실제 SettingsWindow 를 띄워 설정값 로드 배선과 잘못된 저장소 형식의 즉시 안내(네트워크 불필요 경로)를 검증.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class SettingsUpdateUiE2ETests
|
||||
{
|
||||
[Fact]
|
||||
public void 설정창_업데이트_저장소_로드와_형식오류_안내()
|
||||
{
|
||||
// VD_DATA_DIR 격리: SettingsWindow 가 참조하는 프로필 경로를 실사용자와 분리
|
||||
var dataDir = Path.Combine(Path.GetTempPath(), $"vd_set_{Guid.NewGuid():N}");
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", dataDir);
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
sta.Invoke(() =>
|
||||
{
|
||||
var config = new AppConfig { UpdateRepo = "user/videodownloader" };
|
||||
var win = new SettingsWindow(config);
|
||||
|
||||
// 설정 → UI 로드 배선
|
||||
Assert.Equal("user/videodownloader", win.UpdateRepoBox.Text);
|
||||
|
||||
// owner/repo 형식이 아니면 네트워크 호출 없이 즉시 안내
|
||||
win.UpdateRepoBox.Text = "invalid-format";
|
||||
win.CheckUpdate_Click(null, new RoutedEventArgs());
|
||||
Assert.Contains("owner/repo", win.UpdateResultText.Text);
|
||||
|
||||
win.Close();
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", null);
|
||||
try { Directory.Delete(dataDir, recursive: true); } catch (DirectoryNotFoundException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// E2E: 실제 WebView2 + 네이티브 Find API(CoreWebView2.Find) 검증(T1-3).
|
||||
/// WebView2 런타임 위에서 매치 카운트/활성 인덱스가 실제로 발생하는지 확인한다.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class WebView2FindE2ETests
|
||||
{
|
||||
[Fact]
|
||||
public async Task 네이티브_Find로_매치_수와_활성_인덱스를_얻는다()
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
var udf = Path.Combine(Path.GetTempPath(), $"vd_e2e_{Guid.NewGuid():N}");
|
||||
var result = await sta.InvokeAsync(async () =>
|
||||
{
|
||||
var window = new Window();
|
||||
var browser = new Microsoft.Web.WebView2.Wpf.WebView2();
|
||||
window.Content = browser;
|
||||
window.Show();
|
||||
|
||||
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: udf);
|
||||
await browser.EnsureCoreWebView2Async(env);
|
||||
var cv = browser.CoreWebView2;
|
||||
cv.Settings.AreDefaultScriptDialogsEnabled = false;
|
||||
|
||||
var html = "<html><body><p>hello world hello brave hello</p></body></html>";
|
||||
cv.NavigateToString(html);
|
||||
await WaitUntilNavCompleted(cv);
|
||||
|
||||
// 네이티브 Find API — SuppressDefaultFindDialog 로 자체 UI 전제 검증
|
||||
int matchCount = -1, activeIndex = -1;
|
||||
cv.Find.MatchCountChanged += (_, _) => matchCount = cv.Find.MatchCount;
|
||||
cv.Find.ActiveMatchIndexChanged += (_, _) => activeIndex = cv.Find.ActiveMatchIndex;
|
||||
|
||||
var options = env.CreateFindOptions();
|
||||
options.FindTerm = "hello";
|
||||
options.ShouldHighlightAllMatches = true;
|
||||
options.SuppressDefaultFindDialog = true;
|
||||
await cv.Find.StartAsync(options);
|
||||
|
||||
await Wait.Until(() => Task.FromResult(matchCount), c => c > 0, 10000);
|
||||
var countAfterStart = matchCount;
|
||||
|
||||
cv.Find.FindNext();
|
||||
await Wait.Until(() => Task.FromResult(activeIndex), i => i >= 1, 10000);
|
||||
|
||||
cv.Find.Stop();
|
||||
window.Close();
|
||||
return (countAfterStart, activeIndex);
|
||||
});
|
||||
|
||||
try { Directory.Delete(udf, true); } catch { }
|
||||
Assert.Equal(3, result.countAfterStart);
|
||||
Assert.True(result.activeIndex >= 1, $"활성 인덱스가 진행되지 않음: {result.activeIndex}");
|
||||
}
|
||||
|
||||
private static Task WaitUntilNavCompleted(CoreWebView2 cv)
|
||||
{
|
||||
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
EventHandler<CoreWebView2NavigationCompletedEventArgs>? h = null;
|
||||
h = (_, _) => { cv.NavigationCompleted -= h; tcs.TrySetResult(); };
|
||||
cv.NavigationCompleted += h;
|
||||
return tcs.Task.WaitAsync(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실제 사용자 상호작용(인풋창 타이핑, 버튼 클릭, 상태 전이) 복합 유즈케이스 시나리오 E2E 테스트.
|
||||
/// TDD RED 체계 진화: 단편적 검증을 실질적 사용자 스토리로 통폐합.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class UserJourneyInteractiveE2ETests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Journey01_AddressBox_InputAndSearch_Interaction_E2E()
|
||||
{
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), $"vd_journey01_{Guid.NewGuid():N}");
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", tempDir);
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
|
||||
await sta.InvokeAsync(() =>
|
||||
{
|
||||
var window = new MainWindow();
|
||||
window.Show();
|
||||
|
||||
// 1. 주소창 인풋에 텍스트 기록 (URL 타이핑 시뮬레이션)
|
||||
Assert.NotNull(window.AddressBox);
|
||||
window.AddressBox.Text = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
|
||||
Assert.Equal("https://www.youtube.com/watch?v=dQw4w9WgXcQ", window.AddressBox.Text);
|
||||
|
||||
// 2. 텍스트 변경에 따른 도메인 또는 북마크 별표 버튼 갱신 확인
|
||||
Assert.NotNull(window.BookmarkStarBtn);
|
||||
|
||||
// 3. 인풋창 클리어 시뮬레이션
|
||||
window.AddressBox.Text = string.Empty;
|
||||
Assert.Empty(window.AddressBox.Text);
|
||||
|
||||
window.Close();
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Journey02_SidePanel_TabsSwitching_And_FilterInput_E2E()
|
||||
{
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), $"vd_journey02_{Guid.NewGuid():N}");
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", tempDir);
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
|
||||
await sta.InvokeAsync(() =>
|
||||
{
|
||||
var window = new MainWindow();
|
||||
window.Show();
|
||||
|
||||
// 1. 사이드탭 3종(다운로드, 방문기록, 북마크) 존재 확인
|
||||
Assert.NotNull(window.SideTabDownloads);
|
||||
Assert.NotNull(window.SideTabHistory);
|
||||
Assert.NotNull(window.SideTabBookmarks);
|
||||
|
||||
// 2. 방문 기록 탭 클릭(IsChecked = true) 시 방문기록 뷰 활성화 검증
|
||||
window.SideTabHistory.IsChecked = true;
|
||||
Assert.True(window.SideTabHistory.IsChecked);
|
||||
Assert.NotNull(window.HistoryPanelView);
|
||||
Assert.Equal(Visibility.Visible, window.HistoryPanelView.Visibility);
|
||||
|
||||
// 방문기록 검색창에 필터 텍스트 입력
|
||||
Assert.NotNull(window.HistorySearchBox);
|
||||
window.HistorySearchBox.Text = "Github";
|
||||
Assert.Equal("Github", window.HistorySearchBox.Text);
|
||||
|
||||
// 3. 북마크 탭 클릭 시 북마크 뷰 활성화 검증
|
||||
window.SideTabBookmarks.IsChecked = true;
|
||||
Assert.True(window.SideTabBookmarks.IsChecked);
|
||||
Assert.NotNull(window.BookmarksPanelView);
|
||||
Assert.Equal(Visibility.Visible, window.BookmarksPanelView.Visibility);
|
||||
|
||||
// 북마크 검색창에 필터 텍스트 입력
|
||||
Assert.NotNull(window.BookmarkSearchBox);
|
||||
window.BookmarkSearchBox.Text = "React";
|
||||
Assert.Equal("React", window.BookmarkSearchBox.Text);
|
||||
|
||||
// 4. 다운로드 탭 복귀
|
||||
window.SideTabDownloads.IsChecked = true;
|
||||
Assert.NotNull(window.DownloadsPanelView);
|
||||
Assert.Equal(Visibility.Visible, window.DownloadsPanelView.Visibility);
|
||||
|
||||
window.Close();
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue