Skip to content

Commit 92cbbae

Browse files
refactor: replace manual storage polling with React Query in SidePanel
1 parent 4bc98b4 commit 92cbbae

13 files changed

Lines changed: 1068 additions & 58 deletions

File tree

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
3+
import { EmitLog } from '@src/shared/components/EmitLog';
4+
5+
import {
6+
isJstagAvailable,
7+
notifyAutoDetectionFailed,
8+
notifyAutoDetectionSuccess,
9+
pollForJstag,
10+
startAutoDetection,
11+
} from './autoDetection';
12+
13+
vi.mock('@src/shared/components/EmitLog');
14+
15+
describe('autoDetection', () => {
16+
beforeEach(() => {
17+
vi.clearAllMocks();
18+
vi.useFakeTimers();
19+
delete (window as any).jstag;
20+
global.chrome = {
21+
runtime: {
22+
sendMessage: vi.fn().mockResolvedValue({}),
23+
},
24+
storage: {
25+
local: {
26+
get: vi.fn().mockResolvedValue({ extensionState: true }),
27+
set: vi.fn().mockResolvedValue(undefined),
28+
},
29+
},
30+
} as any;
31+
});
32+
33+
describe('isJstagAvailable', () => {
34+
it('should return true when jstag is defined', () => {
35+
(window as any).jstag = {};
36+
expect(isJstagAvailable()).toBe(true);
37+
});
38+
39+
it('should return false when jstag is undefined', () => {
40+
delete (window as any).jstag;
41+
expect(isJstagAvailable()).toBe(false);
42+
});
43+
44+
it('should return false when jstag is null', () => {
45+
(window as any).jstag = null;
46+
expect(isJstagAvailable()).toBe(false);
47+
});
48+
});
49+
50+
describe('notifyAutoDetectionSuccess', () => {
51+
it('should send message to background with correct payload', async () => {
52+
const domain = 'example.com';
53+
await notifyAutoDetectionSuccess(domain);
54+
55+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
56+
action: 'recordDetection',
57+
domain: domain,
58+
confidence: 0.9,
59+
});
60+
expect(EmitLog).toHaveBeenCalledWith({
61+
name: 'content',
62+
payload: { msg: `Notified background of successful detection for: ${domain}` },
63+
});
64+
});
65+
66+
it('should handle sendMessage error gracefully', async () => {
67+
const domain = 'example.com';
68+
const error = new Error('Send failed');
69+
(chrome.runtime.sendMessage as any).mockRejectedValue(error);
70+
71+
await notifyAutoDetectionSuccess(domain);
72+
73+
expect(EmitLog).toHaveBeenCalledWith({
74+
name: 'content',
75+
payload: { msg: 'Error notifying background of successful detection', error: error.message },
76+
});
77+
});
78+
});
79+
80+
describe('notifyAutoDetectionFailed', () => {
81+
it('should send failure message to background', async () => {
82+
const domain = 'example.com';
83+
await notifyAutoDetectionFailed(domain);
84+
85+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
86+
action: 'autoDetectionFailed',
87+
domain: domain,
88+
retryCount: 0,
89+
});
90+
expect(EmitLog).toHaveBeenCalledWith({
91+
name: 'content',
92+
payload: { msg: `Notified background of failed detection for: ${domain}` },
93+
});
94+
});
95+
96+
it('should handle sendMessage error gracefully', async () => {
97+
const domain = 'example.com';
98+
const error = new Error('Send failed');
99+
(chrome.runtime.sendMessage as any).mockRejectedValue(error);
100+
101+
await notifyAutoDetectionFailed(domain);
102+
103+
expect(EmitLog).toHaveBeenCalledWith({
104+
name: 'content',
105+
payload: { msg: 'Error notifying background of failed detection', error: error.message },
106+
});
107+
});
108+
});
109+
110+
describe('pollForJstag', () => {
111+
it('should detect jstag on first check', async () => {
112+
const domain = 'example.com';
113+
(window as any).jstag = {};
114+
115+
pollForJstag(domain);
116+
117+
await vi.runAllTimersAsync();
118+
119+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
120+
action: 'recordDetection',
121+
domain: domain,
122+
confidence: 0.9,
123+
});
124+
});
125+
126+
it('should retry and eventually find jstag', async () => {
127+
const domain = 'example.com';
128+
129+
pollForJstag(domain);
130+
131+
await vi.advanceTimersByTimeAsync(750);
132+
expect(EmitLog).toHaveBeenCalledWith({
133+
name: 'content',
134+
payload: { msg: 'Auto-detection retry 1/5' },
135+
});
136+
137+
(window as any).jstag = {};
138+
await vi.advanceTimersByTimeAsync(750);
139+
140+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
141+
action: 'recordDetection',
142+
domain: domain,
143+
confidence: 0.9,
144+
});
145+
});
146+
147+
it('should fail after max retries', async () => {
148+
const domain = 'example.com';
149+
150+
pollForJstag(domain);
151+
152+
for (let i = 0; i < 5; i++) {
153+
await vi.advanceTimersByTimeAsync(750);
154+
}
155+
156+
expect(EmitLog).toHaveBeenCalledWith({
157+
name: 'content',
158+
payload: { msg: 'Auto-detection failed - no jstag found after max retries' },
159+
});
160+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
161+
action: 'autoDetectionFailed',
162+
domain: domain,
163+
retryCount: 0,
164+
});
165+
});
166+
});
167+
168+
describe('startAutoDetection', () => {
169+
it('should detect jstag immediately if available', async () => {
170+
const domain = 'example.com';
171+
(window as any).jstag = {};
172+
173+
await startAutoDetection(domain);
174+
175+
expect(EmitLog).toHaveBeenCalledWith({
176+
name: 'content',
177+
payload: { msg: 'Lytics jstag found immediately' },
178+
});
179+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
180+
action: 'recordDetection',
181+
domain: domain,
182+
confidence: 0.9,
183+
});
184+
});
185+
186+
it('should start polling if jstag not immediately available', async () => {
187+
const domain = 'example.com';
188+
189+
await startAutoDetection(domain);
190+
191+
expect(EmitLog).toHaveBeenCalledWith({
192+
name: 'content',
193+
payload: { msg: `Starting auto-detection for domain: ${domain}` },
194+
});
195+
196+
(window as any).jstag = {};
197+
await vi.runAllTimersAsync();
198+
199+
expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({
200+
action: 'recordDetection',
201+
domain: domain,
202+
confidence: 0.9,
203+
});
204+
});
205+
});
206+
});

src/pages/content/modules/eventHandler.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('Event Handler Module', () => {
7373

7474
const configData = { cid: 'test-cid', stream: 'test-stream' };
7575
const event = new CustomEvent('config', {
76-
detail: { data: JSON.stringify(configData) },
76+
detail: { data: configData },
7777
});
7878

7979
document.dispatchEvent(event);
@@ -82,10 +82,10 @@ describe('Event Handler Module', () => {
8282

8383
expect(global.chrome.runtime.sendMessage).toHaveBeenCalledWith({
8484
action: 'saveTagConfig',
85-
payload: JSON.stringify(configData),
85+
payload: configData,
8686
});
8787

88-
expect(mockTagConfigStore.set).toHaveBeenCalledWith(JSON.stringify(configData));
88+
expect(mockTagConfigStore.set).toHaveBeenCalledWith(configData);
8989

9090
expect(global.chrome.runtime.sendMessage).toHaveBeenCalledWith({
9191
action: 'recordDetection',
@@ -101,7 +101,7 @@ describe('Event Handler Module', () => {
101101

102102
const configData = { cid: 'test-cid', stream: 'test-stream' };
103103
const event = new CustomEvent('config', {
104-
detail: { data: JSON.stringify(configData) },
104+
detail: { data: configData },
105105
});
106106

107107
document.dispatchEvent(event);
@@ -119,7 +119,7 @@ describe('Event Handler Module', () => {
119119

120120
const configData = { cid: 'test-cid', stream: 'test-stream' };
121121
const event = new CustomEvent('config', {
122-
detail: { data: JSON.stringify(configData) },
122+
detail: { data: configData },
123123
});
124124

125125
document.dispatchEvent(event);
@@ -128,7 +128,7 @@ describe('Event Handler Module', () => {
128128

129129
expect(global.chrome.runtime.sendMessage).toHaveBeenCalledWith({
130130
action: 'saveTagConfig',
131-
payload: JSON.stringify(configData),
131+
payload: configData,
132132
});
133133
});
134134
});
@@ -146,7 +146,7 @@ describe('Event Handler Module', () => {
146146

147147
const entityData = { _uid: 'test-uid', email: 'test@example.com' };
148148
const event = new CustomEvent('entity', {
149-
detail: { data: JSON.stringify(entityData) },
149+
detail: { data: entityData },
150150
});
151151

152152
document.dispatchEvent(event);
@@ -155,10 +155,10 @@ describe('Event Handler Module', () => {
155155

156156
expect(global.chrome.runtime.sendMessage).toHaveBeenCalledWith({
157157
action: 'saveEntity',
158-
payload: JSON.stringify(entityData),
158+
payload: entityData,
159159
});
160160

161-
expect(mockEntityStore.set).toHaveBeenCalledWith(JSON.stringify(entityData));
161+
expect(mockEntityStore.set).toHaveBeenCalledWith(entityData);
162162
});
163163

164164
test('should not process entity event when extension is disabled', async () => {
@@ -168,7 +168,7 @@ describe('Event Handler Module', () => {
168168

169169
const entityData = { _uid: 'test-uid', email: 'test@example.com' };
170170
const event = new CustomEvent('entity', {
171-
detail: { data: JSON.stringify(entityData) },
171+
detail: { data: entityData },
172172
});
173173

174174
document.dispatchEvent(event);
@@ -184,14 +184,14 @@ describe('Event Handler Module', () => {
184184

185185
const entityData = { _uid: 'test-uid', email: 'test@example.com' };
186186
const event = new CustomEvent('entity', {
187-
detail: { data: JSON.stringify(entityData) },
187+
detail: { data: entityData },
188188
});
189189

190190
document.dispatchEvent(event);
191191

192192
await new Promise(resolve => setTimeout(resolve, 0));
193193

194-
expect(mockEntityStore.set).toHaveBeenCalledWith(JSON.stringify(entityData));
194+
expect(mockEntityStore.set).toHaveBeenCalledWith(entityData);
195195
});
196196
});
197197

@@ -203,7 +203,7 @@ describe('Event Handler Module', () => {
203203

204204
const configData = { cid: 'test-cid', stream: 'test-stream' };
205205
const event = new CustomEvent('config', {
206-
detail: { data: JSON.stringify(configData) },
206+
detail: { data: configData },
207207
});
208208

209209
document.dispatchEvent(event);
@@ -220,7 +220,7 @@ describe('Event Handler Module', () => {
220220

221221
const entityData = { _uid: 'test-uid', email: 'test@example.com' };
222222
const event = new CustomEvent('entity', {
223-
detail: { data: JSON.stringify(entityData) },
223+
detail: { data: entityData },
224224
});
225225

226226
document.dispatchEvent(event);
@@ -237,7 +237,7 @@ describe('Event Handler Module', () => {
237237

238238
const configData = { cid: 'test-cid', stream: 'test-stream' };
239239
const event = new CustomEvent('config', {
240-
detail: { data: JSON.stringify(configData) },
240+
detail: { data: configData },
241241
});
242242

243243
document.dispatchEvent(event);

0 commit comments

Comments
 (0)