1package systemviews
2
3import (
4 "strings"
5 "testing"
6 "time"
7
8 "github.com/bnema/dumber/internal/domain/entity"
9 "github.com/stretchr/testify/assert"
10)
11
12func TestHistoryDateKeyAndLabelUsesProvidedNow(t *testing.T) {
13 t.Parallel()
14
15 loc := time.FixedZone("test", 2*60*60)
16 now := time.Date(2026, 4, 24, 12, 0, 0, 0, loc)
17
18 todayKey, todayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 24, 8, 30, 0, 0, loc), now)
19 yesterdayKey, yesterdayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 23, 22, 30, 0, 0, loc), now)
20 olderKey, olderLabel := historyDateKeyAndLabel(time.Date(2026, 4, 20, 9, 0, 0, 0, loc), now)
21
22 assert.Equal(t, "2026-04-24", todayKey)
23 assert.Equal(t, "2026-04-23", yesterdayKey)
24 assert.Equal(t, "2026-04-20", olderKey)
25 assert.Equal(t, "Today", todayLabel)
26 assert.Equal(t, "Yesterday", yesterdayLabel)
27 assert.Equal(t, "Mon Apr 20", olderLabel)
28}
29
30func TestHistorySummaryValuesUsesAllProvidedEntries(t *testing.T) {
31 t.Parallel()
32
33 loc := time.FixedZone("test", 2*60*60)
34 data := historyRenderData{Entries: []*entity.HistoryEntry{
35 {VisitCount: 2, LastVisited: time.Date(2026, 4, 23, 8, 0, 0, 0, loc)},
36 {VisitCount: 3, LastVisited: time.Date(2026, 4, 24, 8, 0, 0, 0, loc)},
37 }}
38
39 entries, visits, days := historySummaryValues(data)
40
41 assert.Equal(t, int64(2), entries)
42 assert.Equal(t, int64(5), visits)
43 assert.Equal(t, int64(2), days)
44}
45
46func TestHistoryShowingLabelDescribesUnpaginatedDefault(t *testing.T) {
47 t.Parallel()
48
49 data := historyRenderData{Entries: []*entity.HistoryEntry{{}, {}}, Limit: 0}
50
51 assert.Equal(t, "Loaded 2 items", historyShowingLabel(data))
52 assert.True(t, disableHistoryNext(data))
53}
54
55func TestHistoryTimelineAppendMergesDuplicateFirstDayGroup(t *testing.T) {
56 t.Parallel()
57
58 loc := time.FixedZone("test", 2*60*60)
59 data := historyRenderData{
60 Entries: []*entity.HistoryEntry{
61 {URL: "https://same.example", Title: "Same day", LastVisited: time.Date(2026, 4, 23, 8, 0, 0, 0, loc)},
62 {URL: "https://older.example", Title: "Older day", LastVisited: time.Date(2026, 4, 22, 8, 0, 0, 0, loc)},
63 },
64 AppendSkipFirstDate: "2026-04-23",
65 }
66
67 html := historyTimelineAppendHTML(data)
68
69 assert.NotContains(t, html, ">Thu Apr 23</h3>")
70 assert.NotContains(t, html, `<section class="sv-timeline-group"><ul class="sv-list">`)
71 assert.Contains(t, html, `data-sv-history-merge-date="2026-04-23"`)
72 assert.Contains(t, html, "Same day")
73 assert.Contains(t, html, `data-sv-history-date="2026-04-22"`)
74 assert.Contains(t, html, ">Wed Apr 22</h3>")
75}
76
77func TestHistoryItemURLTruncatesLongURLs(t *testing.T) {
78 t.Parallel()
79
80 longURL := "https://example.com/" + strings.Repeat("a", historyURLDisplayMaxRunes)
81 entry := &entity.HistoryEntry{URL: longURL}
82
83 displayURL := historyItemURL(entry)
84
85 assert.LessOrEqual(t, len([]rune(displayURL)), historyURLDisplayMaxRunes)
86 assert.Contains(t, displayURL, "…")
87 assert.NotContains(t, displayURL, strings.Repeat("a", historyURLDisplayMaxRunes))
88}
89
90func TestHistoryItemFaviconURLUsesLocalCachedFaviconAPI(t *testing.T) {
91 t.Parallel()
92
93 entry := &entity.HistoryEntry{URL: "https://example.com/docs?token=secret#section"}
94
95 assert.Equal(t, "/api/favicon?domain=example.com&size=32", historyItemFaviconURL(entry))
96}
97
98func TestHistoryItemFaviconURLKeepsPortsForCacheKeys(t *testing.T) {
99 t.Parallel()
100
101 entry := &entity.HistoryEntry{URL: "http://localhost:1455/success?token=secret"}
102
103 assert.Equal(t, "/api/favicon?domain=localhost%3A1455&size=32", historyItemFaviconURL(entry))
104}
105
106func TestHistoryItemFaviconURLIgnoresStoredRemoteFaviconURL(t *testing.T) {
107 t.Parallel()
108
109 entry := &entity.HistoryEntry{
110 URL: "https://example.com/docs",
111 FaviconURL: "https://tracker.example/favicon.png",
112 }
113
114 assert.Equal(t, "/api/favicon?domain=example.com&size=32", historyItemFaviconURL(entry))
115}