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

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,380 @@
# Testing Specification for AI Agents
## Overview
This document provides testing guidelines for AI agents working on the WPF UI project. The project uses separate unit and integration test suites with distinct frameworks and patterns.
## Test Project Structure
### Unit Tests
**Project:** `tests/Wpf.Ui.UnitTests/`
**Target Framework:** `net10.0-windows`
**Project Reference:** `Wpf.Ui`
### Integration Tests
**Project:** `tests/Wpf.Ui.Gallery.IntegrationTests/`
**Target Framework:** `net10.0-windows10.0.26100.0`
**Project References:** `Wpf.Ui.FlaUI`, `Wpf.Ui.Gallery`
## Testing Frameworks
### Unit Testing Stack
- **XUnit v2-style** (PackageReference: `xunit`, `xunit.runner.visualstudio`)
- **NSubstitute 5.3.0** - Mocking framework
- **Standard XUnit Assert** - Assertion library
- **Coverlet 6.0.4** - Code coverage collector
### Integration Testing Stack
- **XUnit v3** (PackageReference: `xunit.v3`, `xunit.runner.visualstudio`)
- **FlaUI.UIA3 5.0.0** - UI automation framework
- **AwesomeAssertions 9.3.0** - Fluent assertion library (FluentAssertions successor)
- **Custom Wpf.Ui.FlaUI** - Custom automation element wrappers
## Unit Test Template
### Naming Convention
```
MethodName_ExpectedResult_WhenCondition
```
**Alternative format:**
```
GivenCondition_MethodName_ExpectedResult
```
### Basic Structure
```csharp
using Xunit;
using NSubstitute;
using Wpf.Ui.Animations; // Namespace under test
namespace Wpf.Ui.UnitTests.Animations;
public class TransitionAnimationProviderTests
{
[Fact]
public void ApplyTransition_ReturnsFalse_WhenDurationIsLessThan10()
{
// Arrange
UIElement mockedUiElement = Substitute.For<UIElement>();
// Act
var result = TransitionAnimationProvider.ApplyTransition(
mockedUiElement,
Transition.FadeIn,
-10
);
// Assert
Assert.False(result);
}
[Fact]
public void ApplyTransition_ReturnsFalse_WhenElementIsNull()
{
// Arrange
UIElement? nullElement = null;
// Act
var result = TransitionAnimationProvider.ApplyTransition(
nullElement,
Transition.FadeIn,
100
);
// Assert
Assert.False(result);
}
}
```
### Mocking with NSubstitute
```csharp
// Create mock
UIElement element = Substitute.For<UIElement>();
// Setup return value
INavigationService service = Substitute.For<INavigationService>();
service.Navigate(typeof(DashboardPage)).Returns(true);
// Verify call
service.Received(1).Navigate(Arg.Any<Type>());
```
### Global Usings
Located in `tests/Wpf.Ui.UnitTests/Usings.cs`:
```csharp
global using System;
global using System.Windows;
global using NSubstitute;
global using Xunit;
```
## Integration Test Template
### Naming Convention
```
Subject_ShouldExpectedBehavior_WhenCondition
```
### Base Class Pattern
All integration tests inherit from `UiTest`:
```csharp
using AwesomeAssertions;
using FlaUI.Core.AutomationElements;
using FlaUI.UIA3.Patterns;
namespace Wpf.Ui.Gallery.IntegrationTests;
public sealed class NavigationTests : UiTest
{
[Fact]
public async Task Settings_ShouldBeAvailable_ThroughAutoSuggestBox()
{
// Arrange
Wpf.Ui.FlaUI.AutoSuggestBox? autoSuggestBox =
FindFirst("NavigationAutoSuggestBox")?.As<AutoSuggestBox>();
autoSuggestBox.Should().NotBeNull(
"because AutoSuggestBox should be present in the navigation bar"
);
// Act
autoSuggestBox!.Enter("Settings");
await Wait(1);
// Assert
TextBox? pageTitle = FindFirst("PageTitle")?.AsTextBox();
pageTitle.Should().NotBeNull();
pageTitle!.Text.Should().Be("Settings");
}
}
```
### UiTest Base Class Features
**Location:** `tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/UiTest.cs`
**Provided Methods:**
```csharp
// Find element by automation ID
protected AutomationElement? FindFirst(string automationId)
// Find element by condition
protected AutomationElement? FindFirst(Func<ConditionFactory, ConditionBase> buildCondition)
// Wait for specified seconds
protected async Task Wait(int seconds, CancellationToken cancellationToken = default)
// Type text
protected void Enter(string text)
// Press key
protected void Press(VirtualKeyShort key)
```
**Lifecycle:**
- `IAsyncLifetime` implementation
- Each test gets its own application instance
- Application launched via `TestedApplication` fixture
- Automatic cleanup after test completion
### AwesomeAssertions Syntax
```csharp
// Null checks
element.Should().NotBeNull("because element must exist");
element.Should().BeNull();
// String assertions
text.Should().Be("Expected");
text.Should().Contain("substring");
text.Should().StartWith("prefix");
// Boolean assertions
condition.Should().BeTrue("because condition must be met");
Application?.HasExited.Should().BeTrue();
// Collection assertions
items.Should().HaveCount(5);
items.Should().Contain(item);
```
### FlaUI Element Access
```csharp
// Find and cast to specific control
Button? button = FindFirst("ButtonId").AsButton();
TextBox? textBox = FindFirst("TextBoxId")?.AsTextBox();
// Custom automation elements
var autoSuggestBox = FindFirst("AutoSuggestBoxId")?.AsAutoSuggestBox();
// Interact with controls
button.Click(moveMouse: false);
textBox.Text = "value";
// Pattern-based interaction
var invokePattern = element.Patterns.Invoke.Pattern;
invokePattern.Invoke();
```
## Running Tests
### Unit Tests
```bash
# Run all unit tests
dotnet test tests/Wpf.Ui.UnitTests/
# Run specific test class
dotnet test tests/Wpf.Ui.UnitTests/ --filter "FullyQualifiedName~TransitionAnimationProviderTests"
# Run with coverage
dotnet test tests/Wpf.Ui.UnitTests/ --collect:"XPlat Code Coverage"
```
### Integration Tests
```bash
# Run all integration tests
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/
# Run specific test
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/ --filter "FullyQualifiedName~TitleBarTests"
# Run with diagnostics
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/ --logger "console;verbosity=detailed"
```
### XUnit Runner Configuration
**Location:** `tests/Wpf.Ui.Gallery.IntegrationTests/xunit.runner.json`
```json
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"parallelizeTestCollections": false,
"diagnosticMessages": true,
"culture": "invariant"
}
```
**Important:** Integration tests do NOT run in parallel due to single Gallery app instance.
## Test Organization
### Namespace Mirroring
Test namespaces mirror source namespaces:
```
src/Wpf.Ui/Animations/TransitionAnimationProvider.cs
tests/Wpf.Ui.UnitTests/Animations/TransitionAnimationProviderTests.cs
```
### File Naming
```
{ClassName}Tests.cs
```
### Test Coverage Areas
**Current Unit Test Coverage:**
- Animations: `TransitionAnimationProvider`
- Extensions: `SymbolExtensions.Swap()`, `SymbolExtensions.GetString()`
**Current Integration Test Coverage:**
- Window title verification
- TitleBar button interaction (Close, Minimize, Maximize)
- Navigation via AutoSuggestBox
- Navigation via NavigationView
- ContentDialog result verification
- ContentDialog keyboard focus isolation
## Guidelines for AI Agents
### When Writing Unit Tests
1. **Mock dependencies** using NSubstitute
2. **Test public API surface** only
3. **Use XUnit Assert methods** for assertions
4. **Follow naming convention** strictly
5. **One assertion per test** (where logical)
6. **Test both success and failure paths**
### When Writing Integration Tests
1. **Inherit from UiTest** base class
2. **Use automation IDs** for element discovery
3. **Add delays with Wait()** for UI updates
4. **Use AwesomeAssertions fluent syntax**
5. **Provide clear "because" messages** in assertions
6. **Clean up state** (handled automatically by fixture)
### Test File Template Locations
**Unit Test Template:**
```
tests/Wpf.Ui.UnitTests/Animations/TransitionAnimationProviderTests.cs
tests/Wpf.Ui.UnitTests/Extensions/SymbolExtensionsTests.cs
```
**Integration Test Template:**
```
tests/Wpf.Ui.Gallery.IntegrationTests/TitleBarTests.cs
tests/Wpf.Ui.Gallery.IntegrationTests/NavigationTests.cs
```
## Common Patterns
### Testing Dependency Properties
```csharp
[Fact]
public void PropertyName_DefaultValue_IsExpected()
{
var control = new MyControl();
Assert.Equal(expectedDefault, control.PropertyName);
}
[Fact]
public void PropertyName_CanBeSet_AndRetrieved()
{
var control = new MyControl();
var expectedValue = new SomeType();
control.PropertyName = expectedValue;
Assert.Equal(expectedValue, control.PropertyName);
}
```
### Testing Services
```csharp
[Fact]
public void Navigate_ReturnsTrue_WhenNavigationSucceeds()
{
// Arrange
var pageProvider = Substitute.For<INavigationViewPageProvider>();
pageProvider.GetPage(Arg.Any<Type>()).Returns(new DashboardPage());
var service = new NavigationService(pageProvider);
var navigationView = Substitute.For<INavigationView>();
service.SetNavigationControl(navigationView);
// Act
bool result = service.Navigate(typeof(DashboardPage));
// Assert
Assert.True(result);
}
```
## Continuous Integration
Tests are NOT currently run in CI (PR validator only builds Gallery app).
**To add test execution to CI**, modify `.github/workflows/wpf-ui-pr-validator.yaml`:
```yaml
- name: Run Unit Tests
run: dotnet test tests/Wpf.Ui.UnitTests/ --no-restore --verbosity normal
- name: Run Integration Tests
run: dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/ --no-restore --verbosity normal
```