Remove the demo text-parsing domain
The scaffolding domain existed to prove the shell end to end before there was
anything real to put in it. There is now, so it goes - as CLAUDE.md promised it
would.
Gone: the two sample parsers, ITextParser, ParsedRecord, the parser catalog,
ParseViewModel and ParseView, their tests, and the settings key that remembered
which parser was last used. ParseError.LineNumber becomes Index, since for a
listing "line 42" was simply untrue, and the error keys move from Parse.Error.*
to Collect.Error.* now that parsing is not a concept here.
Kept: IParser<,>, ParseOutcome, ParseProgress and ParseError. The streaming
contract was always the general part - it was only ever the text-shaped closure
of it that was scaffolding.
Rendering the dashboard caught two keys that were referenced but never added
during the rename: the XAML was repointed and the resources were not. The parity
test could not see it, because it compares the two files against each other and
a key absent from both is consistent. That gap now has its own test, which reads
every {l:Loc} in the XAML and checks it resolves - a screenshot is too late and
too manual a way to find a missing string.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fe62bcf53f
commit
f8744c930a
@@ -19,7 +19,7 @@ public sealed class LocalizationViewTests : IDisposable
|
||||
|
||||
private static (ShellView View, ShellViewModel ViewModel) ShowShell()
|
||||
{
|
||||
var navigation = new NavigationService([new FakePage("Page.Dashboard"), new FakePage("Page.Parse")]);
|
||||
var navigation = new NavigationService([new FakePage("Page.Dashboard"), new FakePage("Page.Collect")]);
|
||||
var viewModel = new ShellViewModel(navigation, new FakeThemeService(), ImmediateSequencer.Instance);
|
||||
var view = new ShellView { DataContext = viewModel };
|
||||
|
||||
@@ -49,14 +49,14 @@ public sealed class LocalizationViewTests : IDisposable
|
||||
var (view, _) = ShowShell();
|
||||
|
||||
var english = RailLabels(view);
|
||||
english.ShouldBe(["Dashboard", "Parse"]);
|
||||
english.ShouldBe(["Dashboard", "Collect"]);
|
||||
|
||||
Localizer.Instance.SetLanguage(AppLanguage.Russian);
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
// The same controls, not a rebuilt tree: this is the whole point of binding through the
|
||||
// localizer's indexer rather than resolving strings once at load.
|
||||
RailLabels(view).ShouldBe(["Обзор", "Разбор"]);
|
||||
RailLabels(view).ShouldBe(["Обзор", "Сбор"]);
|
||||
}
|
||||
|
||||
[AvaloniaFact]
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Headless.XUnit;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.VisualTree;
|
||||
using AvParser.Core.Parsing;
|
||||
using AvParser.Core.Parsing.Samples;
|
||||
using AvParser.Core.Proxies;
|
||||
using AvParser.Core.Settings;
|
||||
using AvParser.UI.ViewModels;
|
||||
using AvParser.UI.Views;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using ReactiveUI.Primitives.Concurrency;
|
||||
|
||||
namespace AvParser.UI.HeadlessTests;
|
||||
|
||||
public class ParseViewTests
|
||||
{
|
||||
private static (ParseView View, ParseViewModel ViewModel, Window Window) ShowPage(params ITextParser[] extra)
|
||||
{
|
||||
var catalog = new ParserCatalog([new DelimitedTextParser(), new KeyValueTextParser(), .. extra]);
|
||||
var lastParser = extra.Length > 0 ? extra[0].Id : null;
|
||||
|
||||
var viewModel = new ParseViewModel(
|
||||
catalog,
|
||||
new FakeSettingsService(new AppSettings { LastParserId = lastParser }),
|
||||
new ProxyPool([], new FakeProxyProbe(), new ProxyOptions()),
|
||||
new EmptyServiceProvider(),
|
||||
NullLogger<ParseViewModel>.Instance,
|
||||
ImmediateSequencer.Instance
|
||||
);
|
||||
|
||||
var view = new ParseView { DataContext = viewModel };
|
||||
var window = new Window
|
||||
{
|
||||
Width = 1400,
|
||||
Height = 900,
|
||||
Content = view,
|
||||
};
|
||||
|
||||
window.Show();
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
return (view, viewModel, window);
|
||||
}
|
||||
|
||||
private static Border Banner(ParseView view) => view.FindControl<Border>("ProxyGateBanner").ShouldNotBeNull();
|
||||
|
||||
[AvaloniaFact]
|
||||
public void No_banner_is_shown_for_a_parser_that_needs_no_network()
|
||||
{
|
||||
var (view, viewModel, _) = ShowPage();
|
||||
|
||||
viewModel.IsBlockedWithoutProxy.ShouldBeFalse();
|
||||
Banner(view).IsVisible.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[AvaloniaFact]
|
||||
public void A_blocked_network_parser_puts_the_banner_on_screen()
|
||||
{
|
||||
// Rendered rather than asserted on the view model: an IsVisible binding that never fires
|
||||
// leaves the page silently unhelpful, which is exactly the failure this guards.
|
||||
var (view, viewModel, _) = ShowPage(new NetworkParser());
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
viewModel.IsBlockedWithoutProxy.ShouldBeTrue();
|
||||
Banner(view).IsEffectivelyVisible.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[AvaloniaFact]
|
||||
public void The_banner_offers_a_way_to_the_proxies_page()
|
||||
{
|
||||
var (view, viewModel, _) = ShowPage(new NetworkParser());
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
var button = Banner(view).GetVisualDescendants().OfType<Button>().ShouldHaveSingleItem();
|
||||
|
||||
button.Command.ShouldBeSameAs(viewModel.GoToProxiesCommand);
|
||||
}
|
||||
|
||||
[AvaloniaFact]
|
||||
public void A_blocked_page_will_not_run_the_parser()
|
||||
{
|
||||
var (view, viewModel, _) = ShowPage(new NetworkParser());
|
||||
viewModel.InputText = "anything";
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
var run = view.GetVisualDescendants()
|
||||
.OfType<Button>()
|
||||
.First(candidate => ReferenceEquals(candidate.Command, viewModel.ParseCommand));
|
||||
|
||||
run.IsEffectivelyEnabled.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private sealed class EmptyServiceProvider : IServiceProvider
|
||||
{
|
||||
public object? GetService(Type serviceType) => null;
|
||||
}
|
||||
|
||||
private sealed class NetworkParser : ITextParser
|
||||
{
|
||||
public string Id => "network";
|
||||
|
||||
public string DisplayName => "Network parser";
|
||||
|
||||
public string Description => "Fetches something";
|
||||
|
||||
public bool RequiresNetwork => true;
|
||||
|
||||
public bool CanParse(string input) => true;
|
||||
|
||||
public async IAsyncEnumerable<ParseOutcome<ParsedRecord>> ParseAsync(
|
||||
string input,
|
||||
IProgress<ParseProgress>? progress,
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user