releases/tests/Paca.Tests/Mobile/MobileDesktopParityBrowserGateTests.cs

270 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
/// <summary>
/// 데스크톱 Paca와 1:1 대응하는 모바일 히스토리, 즐겨찾기, 탭 관리자, 통합 허브 게이트 (Gate 161 ~ Gate 168)
/// </summary>
public class MobileDesktopParityBrowserGateTests
{
private static readonly string MobileProjectDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "src", "Paca.Mobile"));
[Fact]
public void Gate161_DesktopParitySideNavHub_Navigation_Audit()
{
var hub = new DesktopParitySideNavHub();
Assert.Equal(DesktopSideNavTab.Downloads, hub.ActiveTab);
hub.SelectTab(DesktopSideNavTab.History);
Assert.Equal(DesktopSideNavTab.History, hub.ActiveTab);
hub.SelectTab(DesktopSideNavTab.Bookmarks);
Assert.Equal(DesktopSideNavTab.Bookmarks, hub.ActiveTab);
}
[Fact]
public void Gate162_MobileBrowserBottomBarParity_Buttons_Audit()
{
var browserPageCs = File.ReadAllText(Path.Combine(MobileProjectDir, "BrowserPage.cs"));
// 하단 바 7대 핵심 버튼 검증 (뒤로, 앞으로, 홈, 북마크, 히스토리, 다운로드, 탭창)
Assert.Contains("ToolbarBookmarkBtn", browserPageCs);
Assert.Contains("ToolbarHistoryBtn", browserPageCs);
Assert.Contains("ToolbarDownloadsBtn", browserPageCs);
Assert.Contains("ToolbarTabsBtn", browserPageCs);
}
[Fact]
public void Gate163_MobileBookmarkManager_Parity_Audit()
{
var manager = new DesktopParityBookmarkManager();
var url = "https://github.com/videodownloader";
var title = "Paca Video Downloader";
Assert.False(manager.IsBookmarked(url));
// 북마크 토글 (추가)
bool added = manager.ToggleBookmark(url, title);
Assert.True(added);
Assert.True(manager.IsBookmarked(url));
Assert.Single(manager.Bookmarks);
Assert.Equal(title, manager.Bookmarks[0].Title);
// 북마크 토글 (제거)
bool removed = manager.ToggleBookmark(url, title);
Assert.False(removed);
Assert.False(manager.IsBookmarked(url));
Assert.Empty(manager.Bookmarks);
}
[Fact]
public void Gate164_MobileHistoryManager_Parity_Audit()
{
var history = new DesktopParityHistoryManager();
history.RecordVisit("https://youtube.com", "YouTube - Home");
history.RecordVisit("https://naver.com", "NAVER");
history.RecordVisit("https://chzzk.naver.com", "CHZZK Live");
Assert.Equal(3, history.Entries.Count);
// 검색
var searchResult = history.Search("naver");
Assert.Equal(2, searchResult.Count);
// 특정 항목 삭제
history.DeleteEntry("https://naver.com");
Assert.Equal(2, history.Entries.Count);
// 전체 삭제
history.ClearAll();
Assert.Empty(history.Entries);
}
[Fact]
public void Gate165_MobileTabManager_Parity_Audit()
{
var tabManager = new DesktopParityTabManager();
Assert.Single(tabManager.Tabs); // 기본 1개 탭 존재
Assert.NotNull(tabManager.ActiveTab);
// 새 탭 생성
var tab2 = tabManager.CreateTab("https://chzzk.naver.com", "CHZZK");
Assert.Equal(2, tabManager.Tabs.Count);
Assert.Equal(tab2.Id, tabManager.ActiveTab.Id);
// 탭 전환
tabManager.SwitchTab(tabManager.Tabs[0].Id);
Assert.Equal(tabManager.Tabs[0].Id, tabManager.ActiveTab.Id);
// 탭 닫기
tabManager.CloseTab(tab2.Id);
Assert.Single(tabManager.Tabs);
Assert.Equal(tabManager.Tabs[0].Id, tabManager.ActiveTab.Id);
}
[Fact]
public void Gate166_DownloadsPage_DesktopParitySideTabs_Audit()
{
var downloadsPageCs = File.ReadAllText(Path.Combine(MobileProjectDir, "DownloadsPage.cs"));
// 데스크톱과 동일한 사이드 세그먼트 탭(SideTabDownloads, SideTabHistory, SideTabBookmarks) 존재 검증
Assert.Contains("SideTabDownloads", downloadsPageCs);
Assert.Contains("SideTabHistory", downloadsPageCs);
Assert.Contains("SideTabBookmarks", downloadsPageCs);
}
[Fact]
public void Gate167_HistoryBookmarksPage_TabSwitching_Audit()
{
var historyPageCs = File.ReadAllText(Path.Combine(MobileProjectDir, "HistoryBookmarksPage.cs"));
// 방문 기록 & 즐겨찾기 스위처 버튼 검증
Assert.Contains("_tabHistoryBtn", historyPageCs);
Assert.Contains("_tabBookmarkBtn", historyPageCs);
}
[Fact]
public void Gate168_EndToEndDesktopParityFullCycle_Audit()
{
var bookmarkMgr = new DesktopParityBookmarkManager();
var historyMgr = new DesktopParityHistoryManager();
var tabMgr = new DesktopParityTabManager();
var hub = new DesktopParitySideNavHub();
// 1. 첫 탭에서 유튜브 접속 및 방문 기록 추가
tabMgr.ActiveTab.Navigate("https://youtube.com/watch?v=123", "Amazing 4K Video");
historyMgr.RecordVisit(tabMgr.ActiveTab.Url, tabMgr.ActiveTab.Title);
// 2. 즐겨찾기 등록
bookmarkMgr.ToggleBookmark(tabMgr.ActiveTab.Url, tabMgr.ActiveTab.Title);
Assert.True(bookmarkMgr.IsBookmarked(tabMgr.ActiveTab.Url));
// 3. 새 탭 열기
var tab2 = tabMgr.CreateTab("https://naver.com", "NAVER");
historyMgr.RecordVisit(tab2.Url, tab2.Title);
Assert.Equal(2, tabMgr.Tabs.Count);
// 4. 통합 허브에서 방문 기록과 북마크 확인
hub.SelectTab(DesktopSideNavTab.History);
Assert.Equal(DesktopSideNavTab.History, hub.ActiveTab);
Assert.Equal(2, historyMgr.Entries.Count);
hub.SelectTab(DesktopSideNavTab.Bookmarks);
Assert.Equal(DesktopSideNavTab.Bookmarks, hub.ActiveTab);
Assert.Single(bookmarkMgr.Bookmarks);
Assert.Equal("Amazing 4K Video", bookmarkMgr.Bookmarks[0].Title);
}
[Fact]
public void Gate177_MobileBrowserNavigationAndBackKeyLifecycle_Audit()
{
var browserPageCs = File.ReadAllText(Path.Combine(MobileProjectDir, "BrowserPage.cs"));
// 1. Android 하드웨어 Back 키 누름 시 앱 강제 종료 방지 및 웹뷰 뒤로가기/홈 복귀 생명주기 오버라이드 검증
Assert.Contains("protected override bool OnBackButtonPressed()", browserPageCs);
Assert.Contains("CanGoBack", browserPageCs);
Assert.Contains("GoBack()", browserPageCs);
// 2. 툴바 뒤로가기 버튼(‹)에 홈 복귀 폴백이 구현되어 있는지 검증
Assert.Contains("paca://newtab", browserPageCs);
// 3. 독립된 원터치 데스크톱/모바일 모드 토글 버튼 검증
Assert.Contains("_isDesktopMode", browserPageCs);
Assert.Contains("SetDesktopMode", browserPageCs);
}
[Fact]
public void Gate178_MobileWebFullscreenVideo_CustomView_Audit()
{
var customizerCs = File.ReadAllText(Path.Combine(MobileProjectDir, "Platforms", "Android", "PacaAndroidWebViewCustomizer.cs"));
// HTML5 전체화면 비디오 재생 시 뷰 얹기/제거 및 가로모드 전환 생명주기 검증
Assert.Contains("OnShowCustomView", customizerCs);
Assert.Contains("OnHideCustomView", customizerCs);
Assert.Contains("DecorView", customizerCs);
Assert.Contains("RequestedOrientation", customizerCs);
}
[Fact]
public void Gate179_DesignpacaMinimalistUi_Audit()
{
var mobileProjCs = File.ReadAllText(Path.Combine(MobileProjectDir, "Paca.Mobile.csproj"));
var splashSvg = File.ReadAllText(Path.Combine(MobileProjectDir, "Resources", "Splash", "splash.svg"));
var browserPageCs = File.ReadAllText(Path.Combine(MobileProjectDir, "BrowserPage.cs"));
// 1. .NET 기본 스플래시 및 파란색(#2563eb) 완전 제거, 딥 옵시디언 다크(#0A0A0E) 및 Paca 브랜드 스플래시 적용 검증
Assert.DoesNotContain("Color=\"#2563eb\"", mobileProjCs);
Assert.Contains("Color=\"#0A0A0E\"", mobileProjCs);
Assert.DoesNotContain("280.08446 H 195.99044", splashSvg); // .NET 글자 패스 삭제 확인
Assert.Contains("paca-splash-mark", splashSvg);
// 2. 툴바 버튼에서 42x42 네모 상자 Border 컨테이너 제거 및 보더리스 플랫 터치 타깃 검증
Assert.DoesNotContain("BackgroundColor = Color.FromArgb(\"#141420\"),", browserPageCs);
Assert.DoesNotContain("StrokeThickness = 0.5,", browserPageCs);
// 3. 네온 헤어라인 보더(Stroke) 남발 제거 검증
Assert.DoesNotContain("Stroke = Color.FromArgb(\"#8B7CFF\"),", browserPageCs); // 네온 바이올렛 스트로크
Assert.DoesNotContain("Stroke = Color.FromArgb(\"#34D399\"),", browserPageCs); // 네온 그린 스트로크
Assert.DoesNotContain("더블 베젤 앰비언트 글로우", browserPageCs);
// 4. 스피드 다이얼에서 억지 94px 카드 박스 컨테이너 및 내부 이중 박스 제거, 보더리스 플랫 레이아웃 검증
Assert.DoesNotContain("AddLuxurySpeedDialTile", browserPageCs);
Assert.Contains("AddMinimalSpeedDialItem", browserPageCs);
}
[Fact]
public void Gate180_NoEmoji_LucideIconSystem_Audit()
{
var appDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "src", "Paca.App"));
var mobileDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "src", "Paca.Mobile"));
var mobileCoreDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "src", "Paca.Mobile.Core"));
// 1. Lucide 폰트 에셋 존재 및 정상 용량 검증
var mobileFont = Path.Combine(mobileDir, "Resources", "Fonts", "lucide.ttf");
var appFont = Path.Combine(appDir, "Assets", "Fonts", "lucide.ttf");
Assert.True(File.Exists(mobileFont), "Paca.Mobile에 lucide.ttf가 존재해야 합니다.");
Assert.True(File.Exists(appFont), "Paca.App에 lucide.ttf가 존재해야 합니다.");
Assert.True(new FileInfo(mobileFont).Length > 500_000, "lucide.ttf는 500KB 이상의 온전한 폰트 파일이어야 합니다.");
Assert.True(new FileInfo(appFont).Length > 500_000, "lucide.ttf는 500KB 이상의 온전한 폰트 파일이어야 합니다.");
// 2. 모바일 및 모바일 코어 프로덕션 코드 이모지 0건 전면 척결 검증
var emojiRegex = new System.Text.RegularExpressions.Regex(@"[\uD83C-\uDBFF\uDC00-\uDFFF]");
var mobileFiles = Directory.GetFiles(mobileDir, "*.cs", SearchOption.AllDirectories)
.Concat(Directory.GetFiles(mobileDir, "*.xaml", SearchOption.AllDirectories))
.Concat(Directory.GetFiles(mobileCoreDir, "*.cs", SearchOption.AllDirectories))
.Where(f => !f.Contains("\\obj\\") && !f.Contains("\\bin\\"));
foreach (var file in mobileFiles)
{
var text = File.ReadAllText(file);
var match = emojiRegex.Match(text);
Assert.False(match.Success, $"모바일 프로덕션 파일({Path.GetFileName(file)})에 이모지가 잔류해선 안 됩니다: '{match.Value}'");
}
// 3. 데스크톱 앱 프로덕션 코드 이모지 0건 전면 척결 검증
var appFiles = Directory.GetFiles(appDir, "*.cs", SearchOption.AllDirectories)
.Concat(Directory.GetFiles(appDir, "*.xaml", SearchOption.AllDirectories))
.Where(f => !f.Contains("\\obj\\") && !f.Contains("\\bin\\") && !f.Contains("min.js"));
foreach (var file in appFiles)
{
var text = File.ReadAllText(file);
var match = emojiRegex.Match(text);
Assert.False(match.Success, $"데스크톱 프로덕션 파일({Path.GetFileName(file)})에 이모지가 잔류해선 안 됩니다: '{match.Value}'");
}
// 4. 공식 마스코트 스플래시 화면 무결성 검증
var splashSvg = File.ReadAllText(Path.Combine(mobileDir, "Resources", "Splash", "splash.svg"));
Assert.Contains("paca-splash-mark", splashSvg);
Assert.Contains("image/png;base64", splashSvg);
// 5. MobileIconSystem의 Lucide 폰트 패밀리 지정 검증
Assert.Equal("Lucide", MobileIconSystem.FontFamily);
}
}