Files
maraphon-app/tests/Marathon.UI.Tests/Support/FakeResultsBrowsingService.cs
T
alexei.dolgolyov 9f090cec1f feat(phase-8-frontend): results loader UI + browsing list + 41 localization keys
* Pages/Results/ResultsList.razor — completed-events list with date range,
  sport/winner filter, search, footer count.
* Pages/Results/ResultsLoader.razor — driver page with two modes (load all
  in range / load selected events), live progress reporting via
  IProgress<PullResultsProgress>, summary line, cancellable.
* Replaces the Phase 5 Pages/Results.razor placeholder.

Service layer:
* IResultsBrowsingService + ResultsBrowsingService (Scoped, mirrors the
  Event/Anomaly browsing-service pattern). Reads IResultRepository +
  IEventRepository, projects to immutable view-model records.
* UiServicesExtensions: registers ResultsBrowsingService; also fixes an
  unrelated localization resolver bug (drop ResourcesPath since
  SharedResource lives in the Marathon.UI.Resources namespace already).

Localization:
* 41 new Results.* keys (RU+EN parity) covering both pages, filter chips,
  loader modes, progress states, and footer copy.

Tests:
* ResultsListTests + ResultsLoaderTests — 22 new bUnit tests covering
  filter narrowing, mode switching, progress aggregation, and empty
  states.
* FakeResultsBrowsingService support type for tests.
* MarathonTestContext registers the fake; TestData adds factories for
  EventResult/EventResultListItem.
2026-05-09 15:10:49 +03:00

50 lines
1.8 KiB
C#

using Marathon.Application.Storage;
using Marathon.UI.Services;
namespace Marathon.UI.Tests.Support;
/// <summary>
/// In-memory <see cref="IResultsBrowsingService"/> for bUnit tests.
/// Seed via <see cref="ResultItems"/> / <see cref="Candidates"/>.
/// </summary>
public sealed class FakeResultsBrowsingService : IResultsBrowsingService
{
public List<EventResultListItem> ResultItems { get; } = new();
public List<EventResultCandidate> Candidates { get; } = new();
public ResultsFilter? LastResultsFilter { get; private set; }
public DateRange? LastCandidatesRange { get; private set; }
public Task<IReadOnlyList<EventResultListItem>> ListResultsAsync(
ResultsFilter filter,
CancellationToken ct)
{
LastResultsFilter = filter;
IEnumerable<EventResultListItem> q = ResultItems;
if (filter.SportCodes is { Count: > 0 } sports)
q = q.Where(r => sports.Contains(r.Sport.Value));
if (filter.WinnerSide is { } winner)
q = q.Where(r => r.WinnerSide == winner);
if (!string.IsNullOrWhiteSpace(filter.SearchTerm))
{
var t = filter.SearchTerm.Trim();
q = q.Where(r =>
r.LeagueId.Contains(t, StringComparison.OrdinalIgnoreCase) ||
r.Side1Name.Contains(t, StringComparison.OrdinalIgnoreCase) ||
r.Side2Name.Contains(t, StringComparison.OrdinalIgnoreCase));
}
return Task.FromResult<IReadOnlyList<EventResultListItem>>(q.ToList());
}
public Task<IReadOnlyList<EventResultCandidate>> ListLoadCandidatesAsync(
DateRange range,
CancellationToken ct)
{
LastCandidatesRange = range;
return Task.FromResult<IReadOnlyList<EventResultCandidate>>(Candidates.ToList());
}
}