The log showed the contest's columns and nothing else, with the country, points and multiplier columns fixed in the XAML. LogColumns now holds every field the log can show and the settings file keeps the names, left to right. A right click on a header adds or removes a column and offers the contest's own set back, a header can be dragged to move its column, and a click sorts on it. Sorting uses the field, not the text of the cell, so 9,000 kHz comes before 14,000 and the serial numbers do not run 1, 10, 2. Both grids get the same columns, so a value in the pane below sits under the column it belongs to. Removing the last column is refused, otherwise nothing could be selected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
124 lines
4.2 KiB
C#
124 lines
4.2 KiB
C#
using Nonemm.Contests.Rules;
|
|
using Nonemm.Core;
|
|
|
|
namespace Nonemm.Session.Tests;
|
|
|
|
public class LogColumnsTests
|
|
{
|
|
private static readonly StationInfo Me = new()
|
|
{
|
|
Callsign = "DL1ABC",
|
|
CqZone = 14,
|
|
ItuZone = 28,
|
|
Continent = "EU",
|
|
CountryPrefix = "DL",
|
|
};
|
|
|
|
private static IReadOnlyList<QsoColumn> Contest() =>
|
|
new QsoEditor(new CqWorldWide(ModeCategory.Cw), Me, null).Columns;
|
|
|
|
[Fact]
|
|
public void AnEmptyChoiceKeepsTheColumnsTheContestOpensWith() =>
|
|
Assert.Equal(Contest(), LogColumns.Chosen(Contest(), []));
|
|
|
|
[Fact]
|
|
public void TheChosenColumnsComeBackInTheStoredOrder()
|
|
{
|
|
IReadOnlyList<QsoColumn> chosen = LogColumns.Chosen(Contest(), ["Call", "Time", "Comment"]);
|
|
|
|
Assert.Equal([QsoField.Call, QsoField.Time, QsoField.Comment], chosen.Select(c => c.Field));
|
|
}
|
|
|
|
/// The contest names its own exchange columns, so a field it asked for
|
|
/// keeps that label rather than the fallback one.
|
|
[Fact]
|
|
public void AContestColumnKeepsTheLabelTheContestGaveIt()
|
|
{
|
|
QsoColumn zone = LogColumns.Chosen(Contest(), ["Zone"]).Single();
|
|
|
|
Assert.Equal(Contest().Single(c => c.Field == QsoField.Zone).Label, zone.Label);
|
|
}
|
|
|
|
[Fact]
|
|
public void ANameThatIsNotAFieldIsDropped() =>
|
|
Assert.Equal(
|
|
[QsoField.Call],
|
|
LogColumns.Chosen(Contest(), ["Call", "Nonsense"]).Select(c => c.Field));
|
|
|
|
[Fact]
|
|
public void ARepeatedNameOnlyGivesOneColumn() =>
|
|
Assert.Single(LogColumns.Chosen(Contest(), ["Call", "Call"]));
|
|
|
|
/// Every name dropped leaves nothing to show, so the contest's own set is
|
|
/// used rather than an empty grid.
|
|
[Fact]
|
|
public void AListOfNothingUsableFallsBackToTheContest() =>
|
|
Assert.Equal(Contest(), LogColumns.Chosen(Contest(), ["Nonsense"]));
|
|
|
|
[Fact]
|
|
public void TheMenuOffersTheContestColumnsFirstAndThenTheRest()
|
|
{
|
|
IReadOnlyList<QsoColumn> choices = LogColumns.Choices(Contest());
|
|
|
|
Assert.Equal(Contest(), choices.Take(Contest().Count));
|
|
Assert.Equal(choices.Select(c => c.Field).Distinct(), choices.Select(c => c.Field));
|
|
Assert.Contains(QsoField.Comment, choices.Select(c => c.Field));
|
|
}
|
|
|
|
[Fact]
|
|
public void EveryFieldTheLogCanShowReadsBack() =>
|
|
Assert.All(LogColumns.All, c => Assert.NotNull(QsoEditor.Read(Contact(), c.Field)));
|
|
|
|
private static Qso Contact() => new()
|
|
{
|
|
Id = "1",
|
|
TimestampUtc = new DateTime(2026, 5, 30, 12, 34, 56, DateTimeKind.Utc),
|
|
Call = Callsign.Parse("JA1XYZ"),
|
|
Frequency = Frequency.FromKilohertz(14_025),
|
|
Mode = Modes.Cw,
|
|
ContestName = "CQWW",
|
|
Points = 3,
|
|
};
|
|
|
|
/// Sorting on the text of the cell would put 14,000 kHz before 9,000 and
|
|
/// serial number 10 before 2.
|
|
[Fact]
|
|
public void NumbersSortAsNumbersNotAsTheTextTheLogShows()
|
|
{
|
|
Qso low = Contact() with { Frequency = Frequency.FromKilohertz(9_000), ReceivedNumber = 2 };
|
|
Qso high = Contact() with { Frequency = Frequency.FromKilohertz(14_000), ReceivedNumber = 10 };
|
|
|
|
Assert.True(Key(low, QsoField.Frequency).CompareTo(Key(high, QsoField.Frequency)) < 0);
|
|
Assert.True(Key(low, QsoField.ReceivedNumber).CompareTo(Key(high, QsoField.ReceivedNumber)) < 0);
|
|
}
|
|
|
|
[Fact]
|
|
public void TheTimeSortsAsATime()
|
|
{
|
|
Qso first = Contact();
|
|
Qso second = Contact() with { TimestampUtc = first.TimestampUtc.AddHours(1) };
|
|
|
|
Assert.True(Key(first, QsoField.Time).CompareTo(Key(second, QsoField.Time)) < 0);
|
|
}
|
|
|
|
[Fact]
|
|
public void ATextFieldSortsOnWhatTheLogShows() =>
|
|
Assert.Equal(
|
|
QsoEditor.Read(Contact(), QsoField.Call),
|
|
Key(Contact(), QsoField.Call));
|
|
|
|
[Fact]
|
|
public void EveryColumnHasASortKey() =>
|
|
Assert.All(LogColumns.All, c => Assert.NotNull(Key(Contact(), c.Field)));
|
|
|
|
private static IComparable Key(Qso qso, QsoField field) => LogColumns.SortKey(qso, field);
|
|
|
|
[Fact]
|
|
public void StoreAndChosenRoundTrip()
|
|
{
|
|
IReadOnlyList<QsoColumn> chosen = LogColumns.Chosen(Contest(), ["Comment", "Call"]);
|
|
|
|
Assert.Equal(chosen, LogColumns.Chosen(Contest(), LogColumns.Store(chosen)));
|
|
}
|
|
}
|