Files
OpsLog/appearance_test.go
T
rouggy 858c04d267 feat(appearance): colour log rows by QSL status, and fix UDP QSO numbering
New Settings → Appearance section: whole-row colouring in the log grid driven
by QSL / LoTW state, each rule with its own colour from a palette or a free
picker. What Logger32 does, with one difference that matters — the colour is
applied as a 24% tint, not a fill. Logger32's grid is white; this one is dark,
and a saturated user-picked colour behind white text is unreadable at exactly
the moment the operator is scanning for what still needs sending.

Rules are ORDERED and the first match wins, because a contact is usually
several of these at once: one confirmed on LoTW and by card is confirmed, not
"sent, awaiting reply". The order lives in the data so the panel can show the
rules in the order they actually apply, numbered.

The colour is interpolated into a CSS color-mix(), so anything that is not
plainly #rrggbb is refused on the way in and falls back to the default.

Also fixes the QSO number column, which was empty for contacts logged from
WSJT-X: that path inserts through the repo directly and never reached AddQSO.
Rather than chase each of the remaining bulk-insert paths — the POTA hunter
import and the LoTW/QRZ "add what I was missing" passes, which insert OLD
dates and so shift every number after them — the index now checks its length
against a COUNT and rebuilds when they disagree. One indexed count beats
remembering to invalidate in a place that does not exist yet.
2026-08-13 01:06:21 +02:00

52 lines
1.9 KiB
Go

package main
import "testing"
// The colour is interpolated into a CSS color-mix() by the grid, so anything
// that is not plainly a hex colour has to be refused rather than passed on.
func TestRowColorsRefuseAnythingButHex(t *testing.T) {
in := RowColorSettings{Enabled: true, Rules: []RowColorRule{
{ID: "confirmed_lotw", Color: "#123abc", Enabled: true},
{ID: "sent_waiting", Color: "red; background:url(x)", Enabled: true},
{ID: "to_send", Color: "", Enabled: true},
}}
got := normRowColors(in)
byID := map[string]RowColorRule{}
for _, r := range got.Rules {
byID[r.ID] = r
}
if byID["confirmed_lotw"].Color != "#123abc" {
t.Errorf("a valid colour was rewritten: %q", byID["confirmed_lotw"].Color)
}
if byID["sent_waiting"].Color != rowColorDefaults["sent_waiting"] {
t.Errorf("an injection attempt survived: %q", byID["sent_waiting"].Color)
}
if byID["to_send"].Color != rowColorDefaults["to_send"] {
t.Errorf("an empty colour was kept: %q", byID["to_send"].Color)
}
}
// Priority lives in the data, not in a chain of ifs: a contact confirmed on
// LoTW AND by card is confirmed, not "sent, awaiting reply". The panel shows
// the rules in the order they apply, so that order must survive a round trip.
func TestRowColorsKeepPriorityOrder(t *testing.T) {
// Saved in a jumbled order, as a hand-edited settings row could be.
got := normRowColors(RowColorSettings{Rules: []RowColorRule{
{ID: "to_send", Color: "#111111"},
{ID: "confirmed_lotw", Color: "#222222"},
}})
if len(got.Rules) != len(rowColorOrder) {
t.Fatalf("got %d rules, want every one present", len(got.Rules))
}
for i, id := range rowColorOrder {
if got.Rules[i].ID != id {
t.Errorf("rule %d is %q, want %q", i, got.Rules[i].ID, id)
}
}
// The saved colours survived the reordering.
if got.Rules[0].Color != "#222222" {
t.Errorf("confirmed_lotw lost its colour: %q", got.Rules[0].Color)
}
}