更新客户端渲染,更新了壳

This commit is contained in:
QWQLwToo
2026-07-06 23:05:40 +08:00
parent e7dd87bf7e
commit 31d778710b
1311 changed files with 172662 additions and 1582 deletions
@@ -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();
}
}