using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Automation.Provider;
using Newtonsoft.Json.Linq;
namespace XFEWarframe.Desktop.Editing;
internal static class OverviewActionsVerification
{
internal static async Task VerifyAsync(OverviewActionsView view, OverviewActionsModel model, Func<Task<JObject>> inventory, bool dialogs)
{
view.UpdateLayout();
await UntilAsync(() => ReferenceEquals(view.Model, model) && view.ActionButtons.Count == model.Actions.Count, "Overview bindings did not finish loading.");
if (model.Actions.Count != 16 || view.ActionButtons.Count != 16 || view.ActionButtons.Select(x => (string)x.Tag).Distinct().Count() != 16)
throw new InvalidOperationException($"The overview must expose each of the 16 progress operations as its own button: actions={model.Actions.Count}, buttons={view.ActionButtons.Count}, message={model.Message}.");
if (DialogVerification.Children<ListView>(view).Any()) throw new InvalidOperationException("Progress operations are still rendered as an item list.");
var snapshot = await inventory();
var pending = await model.PrepareAsync("unlock.intrinsics") ?? throw new InvalidOperationException(model.Message);
model.Reset(pending.Target with { AccountId = "another-account" });
await model.ExecuteAsync(pending);
if (string.IsNullOrEmpty(model.Message) || !JToken.DeepEquals(snapshot, await inventory())) throw new InvalidOperationException("A prepared operation followed an account switch.");
model.Reset(pending.Target); await model.RefreshAsync();
if (!dialogs) return;
foreach (var expander in DialogVerification.Children<SettingsExpander>(view)) expander.IsExpanded = true;
view.UpdateLayout();
await Task.Delay(400);
foreach (var button in view.ActionButtons.ToArray())
{
if (button.CornerRadius.TopLeft != 8 || button.MinHeight != 40) throw new InvalidOperationException("Overview buttons did not inherit the shared Fluent style.");
var opening = button.OpenAsync();
await UntilAsync(() => button.ActiveDialog?.IsLoaded == true, "Overview action dialog failed to open: " + button.Tag);
if (button.ActiveDialog!.Title?.ToString() != model.Actions.Single(x => x.Key == (string)button.Tag).Label)
throw new InvalidOperationException("Action dialog showed another operation.");
Click(button.ActiveDialog, "CloseButton"); await opening;
}
if (!JToken.DeepEquals(snapshot, await inventory())) throw new InvalidOperationException("Canceling overview actions changed the save.");
foreach (var key in new[] { "unlock.intrinsics", "syndicate" })
{
await UntilAsync(() => !model.IsBusy, "Overview did not return to idle.");
var button = view.ActionButtons.Single(x => (string)x.Tag == key);
var opening = button.OpenAsync();
await UntilAsync(() => button.ActiveDialog?.IsLoaded == true, "Overview action dialog failed to open.");
var dialog = button.ActiveDialog!; dialog.UpdateLayout();
string? selected = null;
if (key == "syndicate")
{
var field = DialogVerification.Children<EditorForm>(dialog).Single().Fields!.Single();
var option = field.Options.First(x => x.Value.ToString().Length > 0); selected = option.Value.ToString();
var input = DialogVerification.Children<AutoSuggestBox>(dialog).Single(); input.Focus(FocusState.Programmatic);
await SuggestionVerification.TypeAsync(input, selected); await SuggestionVerification.SubmitAsync(input);
}
Click(dialog, "PrimaryButton"); await opening;
await UntilAsync(() => !model.IsBusy, "Overview operation did not finish.");
if (model.Message.Length > 0) throw new InvalidOperationException(model.Message);
var result = await inventory();
if (key == "unlock.intrinsics" && (result["PlayerSkills"] is not JObject skills || skills.Count == 0 || skills.Properties().Any(x => x.Value.Value<int>() <= 0)))
throw new InvalidOperationException("The direct intrinsic button did not update the selected save.");
if (key == "syndicate" && result.Value<string>("SupportedSyndicate") != selected)
throw new InvalidOperationException("The overview group selector did not persist its chosen value.");
}
foreach (var expander in DialogVerification.Children<SettingsExpander>(view)) expander.IsExpanded = false;
await Task.Delay(400);
var report = Environment.GetCommandLineArgs().First(x => x.StartsWith("--verify-ui=", StringComparison.Ordinal))["--verify-ui=".Length..];
await OverviewSnapshot.SaveAsync(view.XamlRoot.Content, Path.ChangeExtension(report, ".overview.png"));
}
private static void Click(ContentDialog dialog, string name)
{
var button = DialogVerification.Children<Button>(dialog).Last(x => x.Name == name && x.Visibility == Visibility.Visible && x.ActualHeight > 0);
((IInvokeProvider)new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke)).Invoke();
}
private static async Task UntilAsync(Func<bool> condition, string error)
{
for (var i = 0; i < 500 && !condition(); i++) await Task.Delay(10);
if (!condition()) throw new InvalidOperationException(error);
}
}
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Automation.Provider;
using Newtonsoft.Json.Linq;
namespace XFEWarframe.Desktop.Editing;
internal static class OverviewActionsVerification
{
internal static async Task VerifyAsync(OverviewActionsView view, OverviewActionsModel model, Func<Task<JObject>> inventory, bool dialogs)
{
view.UpdateLayout();
await UntilAsync(() => ReferenceEquals(view.Model, model) && view.ActionButtons.Count == model.Actions.Count, "Overview bindings did not finish loading.");
if (model.Actions.Count != 16 || view.ActionButtons.Count != 16 || view.ActionButtons.Select(x => (string)x.Tag).Distinct().Count() != 16)
throw new InvalidOperationException($"The overview must expose each of the 16 progress operations as its own button: actions={model.Actions.Count}, buttons={view.ActionButtons.Count}, message={model.Message}.");
if (DialogVerification.Children<ListView>(view).Any()) throw new InvalidOperationException("Progress operations are still rendered as an item list.");
var snapshot = await inventory();
var pending = await model.PrepareAsync("unlock.intrinsics") ?? throw new InvalidOperationException(model.Message);
model.Reset(pending.Target with { AccountId = "another-account" });
await model.ExecuteAsync(pending);
if (string.IsNullOrEmpty(model.Message) || !JToken.DeepEquals(snapshot, await inventory())) throw new InvalidOperationException("A prepared operation followed an account switch.");
model.Reset(pending.Target); await model.RefreshAsync();
if (!dialogs) return;
foreach (var expander in DialogVerification.Children<SettingsExpander>(view)) expander.IsExpanded = true;
view.UpdateLayout();
await Task.Delay(400);
foreach (var button in view.ActionButtons.ToArray())
{
if (button.CornerRadius.TopLeft != 8 || button.MinHeight != 40) throw new InvalidOperationException("Overview buttons did not inherit the shared Fluent style.");
var opening = button.OpenAsync();
await UntilAsync(() => button.ActiveDialog?.IsLoaded == true, "Overview action dialog failed to open: " + button.Tag);
if (button.ActiveDialog!.Title?.ToString() != model.Actions.Single(x => x.Key == (string)button.Tag).Label)
throw new InvalidOperationException("Action dialog showed another operation.");
Click(button.ActiveDialog, "CloseButton"); await opening;
}
if (!JToken.DeepEquals(snapshot, await inventory())) throw new InvalidOperationException("Canceling overview actions changed the save.");
foreach (var key in new[] { "unlock.intrinsics", "syndicate" })
{
await UntilAsync(() => !model.IsBusy, "Overview did not return to idle.");
var button = view.ActionButtons.Single(x => (string)x.Tag == key);
var opening = button.OpenAsync();
await UntilAsync(() => button.ActiveDialog?.IsLoaded == true, "Overview action dialog failed to open.");
var dialog = button.ActiveDialog!; dialog.UpdateLayout();
string? selected = null;
if (key == "syndicate")
{
var field = DialogVerification.Children<EditorForm>(dialog).Single().Fields!.Single();
var option = field.Options.First(x => x.Value.ToString().Length > 0); selected = option.Value.ToString();
var input = DialogVerification.Children<AutoSuggestBox>(dialog).Single(); input.Focus(FocusState.Programmatic);
await SuggestionVerification.TypeAsync(input, selected); await SuggestionVerification.SubmitAsync(input);
}
Click(dialog, "PrimaryButton"); await opening;
await UntilAsync(() => !model.IsBusy, "Overview operation did not finish.");
if (model.Message.Length > 0) throw new InvalidOperationException(model.Message);
var result = await inventory();
if (key == "unlock.intrinsics" && (result["PlayerSkills"] is not JObject skills || skills.Count == 0 || skills.Properties().Any(x => x.Value.Value<int>() <= 0)))
throw new InvalidOperationException("The direct intrinsic button did not update the selected save.");
if (key == "syndicate" && result.Value<string>("SupportedSyndicate") != selected)
throw new InvalidOperationException("The overview group selector did not persist its chosen value.");
}
foreach (var expander in DialogVerification.Children<SettingsExpander>(view)) expander.IsExpanded = false;
await Task.Delay(400);
var report = Environment.GetCommandLineArgs().First(x => x.StartsWith("--verify-ui=", StringComparison.Ordinal))["--verify-ui=".Length..];
await OverviewSnapshot.SaveAsync(view.XamlRoot.Content, Path.ChangeExtension(report, ".overview.png"));
}
private static void Click(ContentDialog dialog, string name)
{
var button = DialogVerification.Children<Button>(dialog).Last(x => x.Name == name && x.Visibility == Visibility.Visible && x.ActualHeight > 0);
((IInvokeProvider)new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke)).Invoke();
}
private static async Task UntilAsync(Func<bool> condition, string error)
{
for (var i = 0; i < 500 && !condition(); i++) await Task.Delay(10);
if (!condition()) throw new InvalidOperationException(error);
}
}