更新客户端渲染,更新了壳
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using FlaUI.Core.Definitions;
|
||||
using FlaUI.Core.Input;
|
||||
using FlaUI.Core.WindowsAPI;
|
||||
|
||||
#pragma warning disable IDE0008 // Use explicit type instead of 'var'
|
||||
#pragma warning disable SA1512 // Single-line comments should not be followed by blank line
|
||||
|
||||
namespace Wpf.Ui.Gallery.IntegrationTests;
|
||||
|
||||
public sealed class ContentDialogAutomationTests : UiTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ContentDialog_Should_Return_Correct_Text()
|
||||
{
|
||||
// Give the test app a moment to display the window before starting UI automation interactions
|
||||
await Wait(2, TestContext.Current.CancellationToken);
|
||||
|
||||
// Navigate to the ContentDialog page explicitly: click parent then child nav items
|
||||
var parentNav = FindFirst(c => c.ByText("Dialogs & flyouts"));
|
||||
parentNav.Should().NotBeNull("because the Dialogs & flyouts navigation item should be present");
|
||||
parentNav.Click();
|
||||
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
|
||||
var childNav = FindFirst(c => c.ByText("ContentDialog"));
|
||||
if (childNav == null)
|
||||
{
|
||||
// If the child item is not immediately visible, try toggling the parent to expand it and retry
|
||||
parentNav.Click();
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
childNav = FindFirst(c => c.ByText("ContentDialog"));
|
||||
}
|
||||
|
||||
childNav.Should().NotBeNull("because the ContentDialog navigation item should be present as a child");
|
||||
childNav.Click();
|
||||
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
|
||||
var showButton = FindFirst(c => c.ByText("Show"));
|
||||
showButton
|
||||
.Should()
|
||||
.NotBeNull("because the ContentDialog page must contain a Show button to open the dialog");
|
||||
showButton.AsButton().Click();
|
||||
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
|
||||
// Now exercise each dialog button (Primary, Secondary, Close) and verify
|
||||
// the page TextBlock updates according to ContentDialogViewModel.
|
||||
|
||||
// Primary -> "Save" -> expect "User saved their work"
|
||||
await OpenDialog();
|
||||
await ClickButtonMatching(["Save"]);
|
||||
await WaitForText("User saved their work");
|
||||
|
||||
// Secondary -> "Don't Save" -> expect "User did not save their work"
|
||||
await OpenDialog();
|
||||
await ClickButtonMatching(["Don't Save", "Do not Save", "Dont Save"]);
|
||||
await WaitForText("User did not save their work");
|
||||
|
||||
// Close/Cancel -> "Cancel" -> expect "User cancelled the dialog"
|
||||
await OpenDialog();
|
||||
await ClickButtonMatching(["Cancel", "Close"]);
|
||||
await WaitForText("User cancelled the dialog");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContentDialog_CtrlF_DoesNotFocus_NavigationAutoSuggestBox()
|
||||
{
|
||||
await Wait(2, TestContext.Current.CancellationToken);
|
||||
|
||||
// Open ContentDialog page and show dialog
|
||||
var parentNav = FindFirst(c => c.ByText("Dialogs & flyouts"));
|
||||
parentNav.Should().NotBeNull();
|
||||
parentNav.Click();
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
|
||||
var childNav = FindFirst(c => c.ByText("ContentDialog"));
|
||||
if (childNav == null)
|
||||
{
|
||||
parentNav.Click();
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
childNav = FindFirst(c => c.ByText("ContentDialog"));
|
||||
}
|
||||
|
||||
childNav.Should().NotBeNull();
|
||||
childNav.Click();
|
||||
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
|
||||
var showButton = FindFirst(c => c.ByText("Show"));
|
||||
showButton.Should().NotBeNull();
|
||||
showButton.AsButton().Click();
|
||||
|
||||
await Wait(1, TestContext.Current.CancellationToken);
|
||||
|
||||
// Send Ctrl+F and ensure background autosuggest does not get focused
|
||||
Keyboard.Press(VirtualKeyShort.CONTROL);
|
||||
Keyboard.Type(VirtualKeyShort.KEY_F);
|
||||
Keyboard.Release(VirtualKeyShort.CONTROL);
|
||||
global::FlaUI.Core.Input.Wait.UntilInputIsProcessed();
|
||||
|
||||
// "Find the element with keyboard focus within the main window."
|
||||
var focusedElement = MainWindow
|
||||
?.FindAllDescendants()
|
||||
.FirstOrDefault(e => e.Properties.HasKeyboardFocus.ValueOrDefault);
|
||||
|
||||
// Assert that a focused element is found.
|
||||
focusedElement.Should().NotBeNull("there should be a focused element after sending keys");
|
||||
|
||||
// Get and assert that the AutomationId is not the background autosuggest's id.
|
||||
var focusedAutomationId = focusedElement.Properties.AutomationId.ValueOrDefault as string;
|
||||
focusedAutomationId
|
||||
.Should()
|
||||
.NotBe(
|
||||
"NavigationAutoSuggestBox",
|
||||
"because Ctrl+F should not focus the background autosuggest while the dialog is open"
|
||||
);
|
||||
}
|
||||
|
||||
private Task OpenDialog()
|
||||
{
|
||||
// ensure dialog opened by clicking Show
|
||||
var showButton = FindFirst(c => c.ByText("Show"));
|
||||
if (showButton == null)
|
||||
{
|
||||
// If Show button is not found, assume dialog is already open; give UI a moment to stabilize.
|
||||
return Wait(1, TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
showButton.AsButton().Click();
|
||||
return Wait(1, TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polls the UI until the specified text appears or a retry limit is reached.
|
||||
/// Useful for waiting for view-model-driven UI updates after dialog interactions.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to wait for.</param>
|
||||
/// <param name="retries">Number of polling attempts.</param>
|
||||
/// <param name="delaySeconds">Delay in seconds between attempts (uses test cancellation token).</param>
|
||||
/// <returns>A task that completes when the text is found or throws an assertion if not found.</returns>
|
||||
private async Task WaitForText(string text, int retries = 10, int delaySeconds = 1)
|
||||
{
|
||||
for (var i = 0; i < retries; i++)
|
||||
{
|
||||
if (FindFirst(c => c.ByText(text)) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Wait(delaySeconds, TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
// Final assertion to fail test with clear message if text never appeared
|
||||
FindFirst(c => c.ByText(text))
|
||||
.Should()
|
||||
.NotBeNull($"Expected text '{text}' to appear within timeout");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and clicks a button matching one of the provided candidate texts.
|
||||
/// If no direct match is found, falls back to clicking the last available button in the window.
|
||||
/// </summary>
|
||||
/// <param name="candidates">Array of acceptable button texts (first match is used).</param>
|
||||
/// <returns>A task that waits a short time after clicking to allow UI to settle.</returns>
|
||||
private Task ClickButtonMatching(string[] candidates)
|
||||
{
|
||||
AutomationElement? btn = null;
|
||||
|
||||
foreach (var txt in candidates)
|
||||
{
|
||||
btn = FindFirst(c => c.ByText(txt));
|
||||
if (btn != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (btn == null)
|
||||
{
|
||||
var buttons = MainWindow?.FindAllDescendants(cf => cf.ByControlType(ControlType.Button));
|
||||
if (buttons is { Length: > 0 })
|
||||
{
|
||||
btn = buttons.Last();
|
||||
}
|
||||
}
|
||||
|
||||
btn.Should().NotBeNull($"expected one of: {string.Join(',', candidates)}");
|
||||
btn.AsButton().Click();
|
||||
|
||||
return Wait(1, TestContext.Current.CancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore IDE0008 // Use explicit type instead of 'var'
|
||||
#pragma warning restore SA1512 // Single-line comments should not be followed by blank line
|
||||
@@ -0,0 +1,85 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using FlaUI.Core;
|
||||
using FlaUI.Core.Tools;
|
||||
using FlaUI.UIA3;
|
||||
|
||||
namespace Wpf.Ui.Gallery.IntegrationTests.Fixtures;
|
||||
|
||||
/// <summary>
|
||||
/// Class managing the lifecycle of the tested application implementing <see cref="IAsyncLifetime"/>.
|
||||
/// Uses <see cref="UIA3Automation"/> for UI automation.
|
||||
/// </summary>
|
||||
public sealed class TestedApplication : IAsyncLifetime
|
||||
{
|
||||
private const string ExecutableName = "Wpf.Ui.Gallery.exe";
|
||||
|
||||
private readonly AutomationBase automation = new UIA3Automation();
|
||||
|
||||
private Application? app;
|
||||
|
||||
private Window? mainWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapper for an application which should be automated.
|
||||
/// </summary>
|
||||
public Application? Application => app;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main window of the applications process.
|
||||
/// </summary>
|
||||
public Window? MainWindow => mainWindow ??= app?.GetMainWindow(automation);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask InitializeAsync()
|
||||
{
|
||||
if (app is not null)
|
||||
{
|
||||
app.Close();
|
||||
app.Dispose();
|
||||
}
|
||||
|
||||
string path = Path.Combine(
|
||||
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||
ExecutableName
|
||||
);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to find the application executable at path \"{path}\"."
|
||||
);
|
||||
}
|
||||
|
||||
app = Application.Launch(path);
|
||||
app.WaitWhileMainHandleIsMissing(TimeSpan.FromMinutes(1));
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
if (app is not null)
|
||||
{
|
||||
if (!app.HasExited)
|
||||
{
|
||||
app.Close();
|
||||
}
|
||||
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
Retry.WhileFalse(() => app?.HasExited ?? true, TimeSpan.FromSeconds(2), ignoreException: true);
|
||||
|
||||
app.Dispose();
|
||||
|
||||
app = null;
|
||||
}
|
||||
|
||||
automation?.Dispose();
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Threading;
|
||||
using FlaUI.Core;
|
||||
using FlaUI.Core.Conditions;
|
||||
using FlaUI.Core.Input;
|
||||
using FlaUI.Core.WindowsAPI;
|
||||
|
||||
namespace Wpf.Ui.Gallery.IntegrationTests.Fixtures;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for UI tests implementing <see cref="Xunit.IAsyncLifetime"/> to manage <see cref="FlaUI.Core.Application"/> lifecycle.
|
||||
/// </summary>
|
||||
public abstract class UiTest : IAsyncLifetime
|
||||
{
|
||||
private readonly TestedApplication app = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapper for an application which should be automated.
|
||||
/// </summary>
|
||||
internal Application? Application => app.Application;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main window of the applications process.
|
||||
/// </summary>
|
||||
internal Window? MainWindow => app.MainWindow;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask InitializeAsync() => app.InitializeAsync();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask DisposeAsync() => app.DisposeAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first descendant with the given automation id.
|
||||
/// </summary>
|
||||
/// <param name="automationId">The automation id.</param>
|
||||
/// <returns>The found element or null if no element was found.</returns>
|
||||
protected AutomationElement? FindFirst(string automationId) =>
|
||||
app.MainWindow?.FindFirstDescendant(automationId);
|
||||
|
||||
/// <summary>Finds the first descendant with the condition.</summary>
|
||||
/// <param name="conditionFunc">The condition method.</param>
|
||||
/// <returns>The found element or null if no element was found.</returns>
|
||||
protected AutomationElement? FindFirst(Func<ConditionFactory, ConditionBase> conditionFunc) =>
|
||||
app.MainWindow?.FindFirstDescendant(conditionFunc);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Task that will complete after a time delay.
|
||||
/// </summary>
|
||||
/// <param name="seconds">The time delay in seconds.</param>
|
||||
/// <param name="cancellationToken">An optional cancellation token to cancel the delay.</param>
|
||||
/// <returns>A Task that represents the time delay.</returns>
|
||||
/// <remarks>
|
||||
/// After the specified time delay, the Task is completed in RanToCompletion state. If the <paramref name="cancellationToken"/>
|
||||
/// is cancelled, the returned task will be cancelled and an <see cref="OperationCanceledException"/> will be thrown.
|
||||
/// </remarks>
|
||||
protected Task Wait(int seconds, CancellationToken cancellationToken = default) =>
|
||||
Task.Delay(TimeSpan.FromSeconds(seconds), cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Simulate typing in text. This is slower than setting <see cref="P:FlaUI.Core.AutomationElements.TextBox.Text" /> but raises more events.
|
||||
/// </summary>
|
||||
protected void Enter(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] source = value.Replace("\r\n", "\n").Split('\n');
|
||||
|
||||
Keyboard.Type(source[0]);
|
||||
|
||||
foreach (string text in ((IEnumerable<string>)source).Skip<string>(1))
|
||||
{
|
||||
Keyboard.Type(VirtualKeyShort.RETURN);
|
||||
Keyboard.Type(text);
|
||||
}
|
||||
|
||||
global::FlaUI.Core.Input.Wait.UntilInputIsProcessed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type the given key.
|
||||
/// </summary>
|
||||
protected void Press(VirtualKeyShort virtualKey)
|
||||
{
|
||||
Keyboard.Type(virtualKey);
|
||||
|
||||
global::FlaUI.Core.Input.Wait.UntilInputIsProcessed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
global using System.Reflection;
|
||||
global using AwesomeAssertions;
|
||||
global using FlaUI.Core.AutomationElements;
|
||||
global using Wpf.Ui.FlaUI;
|
||||
global using Wpf.Ui.Gallery.IntegrationTests.Fixtures;
|
||||
@@ -0,0 +1,46 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using FlaUI.Core.Definitions;
|
||||
using FlaUI.Core.WindowsAPI;
|
||||
|
||||
namespace Wpf.Ui.Gallery.IntegrationTests;
|
||||
|
||||
public sealed class NavigationTests : UiTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Settings_ShouldBeAvailable_ThroughAutoSuggestBox()
|
||||
{
|
||||
AutomationElement? autoSuggestBox = FindFirst("NavigationAutoSuggestBox");
|
||||
|
||||
autoSuggestBox
|
||||
.Should()
|
||||
.NotBeNull("because NavigationAutoSuggestBox should be present in the main window");
|
||||
|
||||
autoSuggestBox.As<AutoSuggestBox>().Enter("Settings");
|
||||
|
||||
await Wait(1);
|
||||
|
||||
FindFirst(c => c.ByText("About"))
|
||||
.Should()
|
||||
.NotBeNull("because Settings page should be displayed after clicking the Settings button");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Settings_ShouldBeAvailable_ThroughNavigation()
|
||||
{
|
||||
AutomationElement? settingsButton = FindFirst("NavigationFooterItems")
|
||||
?.FindFirstDescendant(c => c.ByText("Settings"));
|
||||
|
||||
settingsButton.Should().NotBeNull("because NavigationView should be present in the main window");
|
||||
settingsButton.Click();
|
||||
|
||||
await Wait(1);
|
||||
|
||||
FindFirst(c => c.ByText("About"))
|
||||
.Should()
|
||||
.NotBeNull("because Settings page should be displayed after clicking the Settings button");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Windows.Automation;
|
||||
using WindowVisualState = FlaUI.Core.Definitions.WindowVisualState;
|
||||
|
||||
namespace Wpf.Ui.Gallery.IntegrationTests;
|
||||
|
||||
public sealed class TitleBarTests : UiTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task CloseButton_ShouldCloseWindow_WhenClicked()
|
||||
{
|
||||
Button? closeButton = FindFirst("TitleBarCloseButton").AsButton();
|
||||
|
||||
closeButton.Should().NotBeNull("because CloseButton should be present in the main window title bar");
|
||||
closeButton.Click(moveMouse: false);
|
||||
|
||||
await Wait(2);
|
||||
|
||||
Application
|
||||
?.HasExited.Should()
|
||||
.BeTrue("because the main window should be closed after clicking the close button");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MinimizeButton_ShouldHideWindow_WhenClicked()
|
||||
{
|
||||
Button? minimizeButton = FindFirst("TitleBarMinimizeButton").AsButton();
|
||||
|
||||
minimizeButton
|
||||
.Should()
|
||||
.NotBeNull("because MinimizeButton should be present in the main window title bar");
|
||||
minimizeButton.Click(moveMouse: false);
|
||||
|
||||
await Wait(2);
|
||||
|
||||
MainWindow
|
||||
.Patterns.Window.Pattern.WindowVisualState.ValueOrDefault.Should()
|
||||
.Be(
|
||||
WindowVisualState.Minimized,
|
||||
"because the main window should be minimized after clicking the minimize button"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MaximizeButton_ShouldExpandWindow_WhenClicked()
|
||||
{
|
||||
Button? maximizeButton = FindFirst("TitleBarMaximizeButton").AsButton();
|
||||
|
||||
maximizeButton
|
||||
.Should()
|
||||
.NotBeNull("because MaximizeButton should be present in the main window title bar");
|
||||
maximizeButton.Click(moveMouse: false);
|
||||
|
||||
await Wait(2);
|
||||
|
||||
MainWindow
|
||||
.Patterns.Window.Pattern.WindowVisualState.ValueOrDefault.Should()
|
||||
.Be(
|
||||
WindowVisualState.Maximized,
|
||||
"because the main window should be maximized after clicking the maximize button"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
namespace Wpf.Ui.Gallery.IntegrationTests;
|
||||
|
||||
public sealed class WindowTests() : UiTest
|
||||
{
|
||||
[Fact]
|
||||
public void WindowTitle_ShouldMatchPredefinedOne()
|
||||
{
|
||||
string? title = MainWindow?.Title;
|
||||
|
||||
title.Should().Be("WPF UI Gallery", "because the main window title should match the predefined one");
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
|
||||
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AwesomeAssertions" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="FlaUI.UIA3" />
|
||||
<PackageReference Include="xunit.v3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Wpf.Ui.FlaUI\Wpf.Ui.FlaUI.csproj" />
|
||||
<ProjectReference Include="..\..\src\Wpf.Ui.Gallery\Wpf.Ui.Gallery.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="AwesomeAssertions" />
|
||||
<Using Include="NSubstitute" />
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
|
||||
"culture": "invariant",
|
||||
"parallelizeTestCollections": false,
|
||||
"diagnosticMessages": true
|
||||
}
|
||||
Reference in New Issue
Block a user