diff --git a/tests/InputBox.Tests/InputBox.Tests.csproj b/tests/InputBox.Tests/InputBox.Tests.csproj index 4bbd733..ad758fa 100644 --- a/tests/InputBox.Tests/InputBox.Tests.csproj +++ b/tests/InputBox.Tests/InputBox.Tests.csproj @@ -20,7 +20,7 @@ all - + diff --git a/tests/InputBox.Tests/MainFormUiSmokeTests.cs b/tests/InputBox.Tests/MainFormUiSmokeTests.cs index dcb560a..8a004bd 100644 --- a/tests/InputBox.Tests/MainFormUiSmokeTests.cs +++ b/tests/InputBox.Tests/MainFormUiSmokeTests.cs @@ -73,7 +73,7 @@ public MainFormUiSmokeTests() [Trait("Category", "UI")] public void Launch_ShowsPrimaryControls() { - RunWithFailureArtifacts(nameof(Launch_ShowsPrimaryControls), () => + RunWithFailureArtifacts(nameof(Launch_ShowsPrimaryControls), TestContext.Current.CancellationToken, () => { Assert.NotNull(_mainWindow); @@ -98,7 +98,7 @@ public void Launch_ShowsPrimaryControls() [Trait("Category", "UI")] public void ContextMenu_Open_ShowsCoreCommands() { - RunWithFailureArtifacts(nameof(ContextMenu_Open_ShowsCoreCommands), () => + RunWithFailureArtifacts(nameof(ContextMenu_Open_ShowsCoreCommands), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement privacyModeMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_PrivacyMode); @@ -123,7 +123,7 @@ public void ContextMenu_Open_ShowsCoreCommands() [Trait("Category", "UI")] public void HelpDialog_Open_And_CloseSuccessfully() { - RunWithFailureArtifacts(nameof(HelpDialog_Open_And_CloseSuccessfully), () => + RunWithFailureArtifacts(nameof(HelpDialog_Open_And_CloseSuccessfully), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement helpMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_Help); @@ -159,7 +159,7 @@ public void HelpDialog_Open_And_CloseSuccessfully() [Trait("Category", "UI")] public void MinimizeOnReturn_ConfirmDialog_Open_And_CancelSuccessfully() { - RunWithFailureArtifacts(nameof(MinimizeOnReturn_ConfirmDialog_Open_And_CancelSuccessfully), () => + RunWithFailureArtifacts(nameof(MinimizeOnReturn_ConfirmDialog_Open_And_CancelSuccessfully), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement minimizeOnReturnMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_MinimizeOnReturn); @@ -193,7 +193,7 @@ public void MinimizeOnReturn_ConfirmDialog_Open_And_CancelSuccessfully() [Trait("Category", "UI")] public void SettingsGamepadSubMenu_Open_ShowsCoreCommands() { - RunWithFailureArtifacts(nameof(SettingsGamepadSubMenu_Open_ShowsCoreCommands), () => + RunWithFailureArtifacts(nameof(SettingsGamepadSubMenu_Open_ShowsCoreCommands), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement settingsMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_Settings); @@ -225,7 +225,7 @@ public void SettingsGamepadSubMenu_Open_ShowsCoreCommands() [Trait("Category", "UI")] public void GamepadCalibrationDialog_Open_And_CloseSuccessfully() { - RunWithFailureArtifacts(nameof(GamepadCalibrationDialog_Open_And_CloseSuccessfully), () => + RunWithFailureArtifacts(nameof(GamepadCalibrationDialog_Open_And_CloseSuccessfully), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement settingsMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_Settings); @@ -266,7 +266,7 @@ public void GamepadCalibrationDialog_Open_And_CloseSuccessfully() [Trait("Category", "UI")] public void PhraseSubMenu_Open_ShowsManagementCommands() { - RunWithFailureArtifacts(nameof(PhraseSubMenu_Open_ShowsManagementCommands), () => + RunWithFailureArtifacts(nameof(PhraseSubMenu_Open_ShowsManagementCommands), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement phrasesMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_Phrases); @@ -294,7 +294,7 @@ public void PhraseSubMenu_Open_ShowsManagementCommands() [Trait("Category", "UI")] public void PhraseManagerDialog_Open_And_CloseSuccessfully() { - RunWithFailureArtifacts(nameof(PhraseManagerDialog_Open_And_CloseSuccessfully), () => + RunWithFailureArtifacts(nameof(PhraseManagerDialog_Open_And_CloseSuccessfully), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement phrasesMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_Phrases); @@ -333,7 +333,7 @@ public void PhraseManagerDialog_Open_And_CloseSuccessfully() [Trait("Category", "UI")] public void PhraseEditDialog_Open_And_CancelSuccessfully() { - RunWithFailureArtifacts(nameof(PhraseEditDialog_Open_And_CancelSuccessfully), () => + RunWithFailureArtifacts(nameof(PhraseEditDialog_Open_And_CancelSuccessfully), TestContext.Current.CancellationToken, () => { AutomationElement contextMenu = OpenContextMenu(); AutomationElement phrasesMenuItem = FindMenuItemByLabel(contextMenu, Strings.Menu_Phrases); @@ -386,7 +386,7 @@ public void PhraseEditDialog_Open_And_CancelSuccessfully() [Trait("Category", "UI")] public void CopyButton_Click_ClearsInput() { - RunWithFailureArtifacts(nameof(CopyButton_Click_ClearsInput), () => + RunWithFailureArtifacts(nameof(CopyButton_Click_ClearsInput), TestContext.Current.CancellationToken, () => { FlaUiTextBox inputTextBox = FindInputBox(); FlaUiButton copyButton = FindCopyButton(); @@ -417,7 +417,7 @@ public void CopyButton_Click_ClearsInput() [Trait("Category", "UI")] public void RestartPrompt_ConfirmYes_RelaunchedWindowStaysForeground() { - RunWithFailureArtifacts(nameof(RestartPrompt_ConfirmYes_RelaunchedWindowStaysForeground), () => + RunWithFailureArtifacts(nameof(RestartPrompt_ConfirmYes_RelaunchedWindowStaysForeground), TestContext.Current.CancellationToken, () => { Assert.NotNull(_application); Assert.NotNull(_mainWindow); @@ -1174,9 +1174,12 @@ private static void WaitUntil( /// 在測試失敗時擷取桌面畫面與診斷文字,方便 GitHub Actions 回傳 Artifact。 /// /// 目前執行中的測試名稱。 + /// xUnit 測試逾時所提供的取消權杖。 /// 實際要執行的測試邏輯。 - private void RunWithFailureArtifacts(string testName, Action testAction) + private void RunWithFailureArtifacts(string testName, CancellationToken cancellationToken, Action testAction) { + cancellationToken.ThrowIfCancellationRequested(); + try { MarkStep($"{testName}: started"); diff --git a/tests/InputBox.Tests/README.md b/tests/InputBox.Tests/README.md index 94392dc..9f2df4b 100644 --- a/tests/InputBox.Tests/README.md +++ b/tests/InputBox.Tests/README.md @@ -142,6 +142,8 @@ xUnit v3 為每個 `[Fact]` 建立獨立的測試類別實例,`IDisposable.Dis > 為避免 hosted runner 與本機 Agent 因自訂 modal dialog 的偵測不穩而長時間卡住,目前所有 UI 案例皆加上 fail-fast 逾時保護;dialog 案例會在此保護下持續驗證。 +> UI 冒煙測試會將 xUnit 測試逾時的 `TestContext.Current.CancellationToken` 傳入共用執行流程,確保 xUnit v3 MTP v2 4.0.0 能在逾時後及時終止測試。 + > UI 元素比對一律優先使用資源檔的目前語系字串與 AutomationId,而非寫死繁中或英文標籤,因此 Hosted Runner 的系統語系變動不應成為這組 smoke test 的失敗來源。 ### 6. 目標框架