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
|
import { describe, it, expect } from 'vitest';
import { Store } from '../store';
import type { Item, Feed, Category } from '../types';
function makeItem(id: number): Item {
return {
_id: id,
feed_id: 1,
title: `Test Item ${id}`,
url: `https://example.com/item/${id}`,
description: `Description for item ${id}`,
publish_date: '2024-01-01T00:00:00Z',
read: false,
starred: false,
feed_title: 'Test Feed',
};
}
function makeFeed(id: number): Feed {
return {
_id: id,
url: `https://example.com/feed/${id}`,
web_url: `https://example.com/${id}`,
title: `Feed ${id}`,
category: `cat-${id % 5}`,
};
}
describe('store performance', () => {
it('setItems with 500 items + event dispatch under 10ms', () => {
const store = new Store();
const items = Array.from({ length: 500 }, (_, i) => makeItem(i));
let eventFired = false;
store.on('items-updated', () => { eventFired = true; });
const start = performance.now();
store.setItems(items);
const elapsed = performance.now() - start;
expect(store.items.length).toBe(500);
expect(eventFired).toBe(true);
expect(elapsed).toBeLessThan(10);
});
it('setItems append 500 items to existing 500 under 10ms', () => {
const store = new Store();
const initial = Array.from({ length: 500 }, (_, i) => makeItem(i));
const more = Array.from({ length: 500 }, (_, i) => makeItem(i + 500));
store.setItems(initial);
const start = performance.now();
store.setItems(more, true);
const elapsed = performance.now() - start;
expect(store.items.length).toBe(1000);
expect(elapsed).toBeLessThan(10);
});
it('setFeeds with 200 feeds under 5ms', () => {
const store = new Store();
const feeds = Array.from({ length: 200 }, (_, i) => makeFeed(i));
let eventFired = false;
store.on('feeds-updated', () => { eventFired = true; });
const start = performance.now();
store.setFeeds(feeds);
const elapsed = performance.now() - start;
expect(store.feeds.length).toBe(200);
expect(eventFired).toBe(true);
expect(elapsed).toBeLessThan(5);
});
it('rapid filter changes (100 toggles) under 50ms', () => {
const store = new Store();
const filters: Array<'unread' | 'all' | 'starred'> = ['unread', 'all', 'starred'];
let eventCount = 0;
store.on('filter-updated', () => { eventCount++; });
const start = performance.now();
for (let i = 0; i < 100; i++) {
store.setFilter(filters[i % 3]);
}
const elapsed = performance.now() - start;
expect(eventCount).toBeGreaterThan(0);
expect(elapsed).toBeLessThan(50);
});
it('rapid search query changes (100 updates) under 50ms', () => {
const store = new Store();
let eventCount = 0;
store.on('search-updated', () => { eventCount++; });
const start = performance.now();
for (let i = 0; i < 100; i++) {
store.setSearchQuery(`query-${i}`);
}
const elapsed = performance.now() - start;
expect(eventCount).toBe(100);
expect(elapsed).toBeLessThan(50);
});
it('multiple listeners (50) on items-updated under 10ms', () => {
const store = new Store();
const items = Array.from({ length: 100 }, (_, i) => makeItem(i));
let totalCalls = 0;
for (let i = 0; i < 50; i++) {
store.on('items-updated', () => { totalCalls++; });
}
const start = performance.now();
store.setItems(items);
const elapsed = performance.now() - start;
expect(totalCalls).toBe(50);
expect(elapsed).toBeLessThan(10);
});
});
|