File size: 5,529 Bytes
10d1fd4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import { describe, expect, it } from "vitest";
import {
  formatRelativeTime,
  getHostname,
  getSemanticVersion,
  groupSearchResultsByDate,
  searchWithFuzzy,
} from "./stringFormatters";

describe("stringFormatters", () => {
  describe("getHostname", () => {
    it("should extract hostname from valid URL", () => {
      expect(getHostname("https://example.com/page")).toBe("example.com");
    });

    it("should remove www prefix", () => {
      expect(getHostname("https://www.example.com/page")).toBe("example.com");
    });

    it("should handle http URLs", () => {
      expect(getHostname("http://test.org/path")).toBe("test.org");
    });

    it("should return original string for invalid URL", () => {
      expect(getHostname("not-a-url")).toBe("not-a-url");
    });

    it("should handle URL with port (hostname only)", () => {
      expect(getHostname("https://localhost:3000")).toBe("localhost");
    });
  });

  describe("getSemanticVersion", () => {
    it("should convert number timestamp to YYYY.MM.DD format", () => {
      const result = getSemanticVersion(1700000000000);
      expect(result).toMatch(/^\d{4}\.\d{1,2}\.\d{1,2}$/);
    });

    it("should handle Date object", () => {
      const result = getSemanticVersion(new Date(2024, 0, 15));
      expect(result).toBe("2024.1.15");
    });

    it("should handle ISO date string", () => {
      const result = getSemanticVersion("2024-06-01");
      expect(result).toMatch(/^2024\.6\.1$/);
    });
  });

  describe("formatRelativeTime", () => {
    it("should return Just now for recent timestamp", () => {
      const now = Date.now();
      expect(formatRelativeTime(now)).toBe("Just now");
    });

    it("should return minutes ago", () => {
      const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
      expect(formatRelativeTime(fiveMinutesAgo)).toBe("5m ago");
    });

    it("should return hours ago", () => {
      const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000;
      expect(formatRelativeTime(twoHoursAgo)).toBe("2h ago");
    });

    it("should return days ago", () => {
      const threeDaysAgo = Date.now() - 3 * 24 * 60 * 60 * 1000;
      expect(formatRelativeTime(threeDaysAgo)).toBe("3d ago");
    });

    it("should return locale date for older timestamps", () => {
      const oldTimestamp = Date.now() - 30 * 24 * 60 * 60 * 1000;
      const result = formatRelativeTime(oldTimestamp);
      expect(result).not.toContain("ago");
    });
  });

  describe("searchWithFuzzy", () => {
    const items = [
      { id: 1, name: "apple" },
      { id: 2, name: "application" },
      { id: 3, name: "banana" },
      { id: 4, name: "blueprint" },
    ];

    it("should return all items with score 0 for empty query", () => {
      const results = searchWithFuzzy(items, "", (item) => item.name);
      expect(results).toHaveLength(4);
      expect(results.every((r) => r.score === 0)).toBe(true);
    });

    it("should find matches with fuzzy search", () => {
      const results = searchWithFuzzy(items, "app", (item) => item.name);
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].item.name).toContain("app");
    });

    it("should respect limit parameter", () => {
      const results = searchWithFuzzy(items, "a", (item) => item.name, 2);
      expect(results).toHaveLength(2);
    });

    it("should return empty array for no matches", () => {
      const results = searchWithFuzzy(items, "xyz", (item) => item.name);
      expect(results).toHaveLength(0);
    });

    it("should return scores between 0 and 1", () => {
      const results = searchWithFuzzy(items, "a", (item) => item.name);
      results.forEach((r) => {
        expect(r.score).toBeGreaterThanOrEqual(0);
        expect(r.score).toBeLessThanOrEqual(1);
      });
    });
  });

  describe("groupSearchResultsByDate", () => {
    it("should group items by Today", () => {
      const now = Date.now();
      const items = [{ item: { id: 1 }, timestamp: now }];
      const groups = groupSearchResultsByDate(items);
      expect(groups.Today).toHaveLength(1);
    });

    it("should group items by Yesterday", () => {
      const yesterday = Date.now() - 24 * 60 * 60 * 1000;
      const items = [{ item: { id: 1 }, timestamp: yesterday }];
      const groups = groupSearchResultsByDate(items);
      expect(groups.Yesterday).toHaveLength(1);
    });

    it("should group items by This Week", () => {
      const threeDaysAgo = Date.now() - 3 * 24 * 60 * 60 * 1000;
      const items = [{ item: { id: 1 }, timestamp: threeDaysAgo }];
      const groups = groupSearchResultsByDate(items);
      expect(groups["This Week"]).toHaveLength(1);
    });

    it("should group older items by month", () => {
      const oldTimestamp = Date.now() - 30 * 24 * 60 * 60 * 1000;
      const items = [{ item: { id: 1 }, timestamp: oldTimestamp }];
      const groups = groupSearchResultsByDate(items);
      const keys = Object.keys(groups);
      expect(keys.length).toBe(1);
      expect(keys[0]).toMatch(/^\w{3} \d{4}$/);
    });

    it("should handle empty array", () => {
      const groups = groupSearchResultsByDate([]);
      expect(Object.keys(groups)).toHaveLength(0);
    });

    it("should group multiple items in same category", () => {
      const now = Date.now();
      const items = [
        { item: { id: 1 }, timestamp: now },
        { item: { id: 2 }, timestamp: now - 60 * 1000 },
      ];
      const groups = groupSearchResultsByDate(items);
      expect(groups.Today).toHaveLength(2);
    });
  });
});