Skip to content

Commit 10be2a4

Browse files
committed
feat(options): implement test webhook button functionality and validation
1 parent e8534fc commit 10be2a4

File tree

2 files changed

+261
-2
lines changed

2 files changed

+261
-2
lines changed

options/options.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,16 +1093,25 @@ testWebhookBtn.addEventListener('click', async () => {
10931093
return;
10941094
}
10951095

1096+
// Create webhook object with all current form settings
10961097
const webhook = {
10971098
url: url,
10981099
method: methodSelect.value,
10991100
headers: [...headers],
1100-
// Add other properties as needed
1101+
identifier: identifierInput.value.trim() || undefined,
1102+
customPayload: customPayloadInput.value.trim() || undefined,
1103+
urlFilter: urlFilterInput.value.trim() || undefined,
1104+
groupId: groupSelect.value || undefined
11011105
};
1106+
1107+
// Add test header to identify this as a test webhook
1108+
webhook.headers = [...webhook.headers, { key: 'x-webhook-test', value: 'true' }];
1109+
11021110
formStatusMessage.className = 'status-message';
1111+
testWebhookBtn.disabled = true;
11031112

11041113
try {
1105-
await sendWebhook(webhook, true);
1114+
await sendWebhook(webhook, false); // Use false to send real data instead of test payload
11061115
formStatusMessage.textContent = browser.i18n.getMessage('optionsTestSuccess');
11071116
formStatusMessage.classList.add('success');
11081117
} catch (error) {

tests/options.test.js

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,254 @@ describe('options page', () => {
264264
expect(document.getElementById('add-webhook-form').classList.contains('hidden')).toBe(false);
265265
expect(document.getElementById('add-new-webhook-btn').classList.contains('hidden')).toBe(true);
266266
});
267+
268+
describe('test webhook button', () => {
269+
let mockSendWebhook;
270+
let mockAlert;
271+
272+
beforeEach(() => {
273+
// Mock the sendWebhook function
274+
mockSendWebhook = jest.fn();
275+
global.sendWebhook = mockSendWebhook;
276+
277+
// Mock alert
278+
mockAlert = jest.fn();
279+
global.alert = mockAlert;
280+
281+
// Properly initialize the method select element
282+
const methodSelect = document.getElementById('webhook-method');
283+
methodSelect.innerHTML = '<option value="POST">POST</option><option value="GET">GET</option>';
284+
methodSelect.value = 'POST'; // Set default value
285+
286+
// Update the global browser object with correct i18n mock
287+
global.browser.i18n.getMessage = jest.fn((key) => {
288+
const messages = {
289+
'optionsTestSuccess': 'Test webhook sent successfully.',
290+
'optionsTestError': 'Test failed: '
291+
};
292+
return messages[key] || key;
293+
});
294+
295+
// Also update the getBrowserAPI mock to return the updated browser object
296+
global.window.getBrowserAPI = jest.fn().mockReturnValue(global.browser);
297+
298+
// Mock setTimeout
299+
jest.useFakeTimers();
300+
});
301+
302+
afterEach(() => {
303+
jest.useRealTimers();
304+
jest.restoreAllMocks();
305+
});
306+
307+
test('test button is hidden initially', () => {
308+
const testBtn = document.getElementById('test-webhook-btn');
309+
expect(testBtn.classList.contains('hidden')).toBe(true);
310+
});
311+
312+
test('test button becomes visible when adding new webhook', () => {
313+
const addBtn = document.getElementById('add-new-webhook-btn');
314+
const testBtn = document.getElementById('test-webhook-btn');
315+
316+
addBtn.click();
317+
318+
expect(testBtn.classList.contains('hidden')).toBe(false);
319+
});
320+
321+
test('test button requires URL before sending webhook', async () => {
322+
const addBtn = document.getElementById('add-new-webhook-btn');
323+
const testBtn = document.getElementById('test-webhook-btn');
324+
const urlInput = document.getElementById('webhook-url');
325+
326+
// Show the form
327+
addBtn.click();
328+
329+
// Clear URL and click test button
330+
urlInput.value = '';
331+
testBtn.click();
332+
333+
expect(mockAlert).toHaveBeenCalledWith('URL is required to send a test webhook.');
334+
expect(mockSendWebhook).not.toHaveBeenCalled();
335+
});
336+
337+
test('test button sends webhook with all form settings and test header', async () => {
338+
const addBtn = document.getElementById('add-new-webhook-btn');
339+
const testBtn = document.getElementById('test-webhook-btn');
340+
const urlInput = document.getElementById('webhook-url');
341+
const methodSelect = document.getElementById('webhook-method');
342+
const identifierInput = document.getElementById('webhook-identifier');
343+
const customPayloadInput = document.getElementById('webhook-custom-payload');
344+
const groupSelect = document.getElementById('webhook-group');
345+
346+
// Mock successful webhook send
347+
mockSendWebhook.mockResolvedValue();
348+
349+
// Show the form
350+
addBtn.click();
351+
352+
// Fill in form data
353+
urlInput.value = 'https://test-webhook.com/endpoint';
354+
methodSelect.value = 'POST'; // This should now work properly
355+
identifierInput.value = 'test-identifier';
356+
customPayloadInput.value = '{"custom": "data"}';
357+
groupSelect.value = '';
358+
359+
// Add a custom header
360+
const headerKeyInput = document.getElementById('header-key');
361+
const headerValueInput = document.getElementById('header-value');
362+
const addHeaderBtn = document.getElementById('add-header-btn');
363+
364+
headerKeyInput.value = 'Authorization';
365+
headerValueInput.value = 'Bearer token123';
366+
addHeaderBtn.click();
367+
368+
// Click test button
369+
await testBtn.click();
370+
371+
// Verify sendWebhook was called with correct parameters
372+
expect(mockSendWebhook).toHaveBeenCalledWith({
373+
url: 'https://test-webhook.com/endpoint',
374+
method: 'POST', // This should now match
375+
headers: [
376+
{ key: 'Authorization', value: 'Bearer token123' },
377+
{ key: 'x-webhook-test', value: 'true' }
378+
],
379+
identifier: 'test-identifier',
380+
customPayload: '{"custom": "data"}',
381+
urlFilter: undefined,
382+
groupId: undefined
383+
}, false); // false = send real data, not test payload
384+
});
385+
386+
test('test button shows success message on successful webhook', async () => {
387+
const addBtn = document.getElementById('add-new-webhook-btn');
388+
const testBtn = document.getElementById('test-webhook-btn');
389+
const urlInput = document.getElementById('webhook-url');
390+
const statusMessage = document.getElementById('form-status-message');
391+
392+
// Mock successful webhook send
393+
mockSendWebhook.mockResolvedValue();
394+
395+
// Show the form
396+
addBtn.click();
397+
urlInput.value = 'https://test.com';
398+
399+
// Click test button
400+
await testBtn.click();
401+
402+
expect(statusMessage.textContent).toBe('Test webhook sent successfully.');
403+
expect(statusMessage.classList.contains('success')).toBe(true);
404+
expect(testBtn.disabled).toBe(true);
405+
406+
// Fast-forward timers to check button re-enabling
407+
jest.advanceTimersByTime(2500);
408+
expect(testBtn.disabled).toBe(false);
409+
expect(statusMessage.textContent).toBe('');
410+
});
411+
412+
test('test button shows error message on webhook failure', async () => {
413+
const addBtn = document.getElementById('add-new-webhook-btn');
414+
const testBtn = document.getElementById('test-webhook-btn');
415+
const urlInput = document.getElementById('webhook-url');
416+
const statusMessage = document.getElementById('form-status-message');
417+
418+
// Mock failed webhook send
419+
const testError = new Error('Network error');
420+
mockSendWebhook.mockRejectedValue(testError);
421+
422+
// Show the form
423+
addBtn.click();
424+
urlInput.value = 'https://test.com';
425+
426+
// Click test button
427+
await testBtn.click();
428+
429+
expect(statusMessage.textContent).toBe('Test failed: Network error');
430+
expect(statusMessage.classList.contains('error')).toBe(true);
431+
expect(testBtn.disabled).toBe(true);
432+
433+
// Fast-forward timers to check button re-enabling
434+
jest.advanceTimersByTime(2500);
435+
expect(testBtn.disabled).toBe(false);
436+
expect(statusMessage.textContent).toBe('');
437+
});
438+
439+
test('test button becomes visible when editing existing webhook', async () => {
440+
const testBtn = document.getElementById('test-webhook-btn');
441+
442+
// Mock existing webhook data
443+
global.getBrowserAPI = () => ({
444+
storage: {
445+
sync: {
446+
get: jest.fn().mockResolvedValue({
447+
webhooks: [{
448+
id: 'test-id',
449+
label: 'Test Webhook',
450+
url: 'https://example.com',
451+
method: 'POST',
452+
headers: [],
453+
identifier: 'test'
454+
}]
455+
})
456+
}
457+
},
458+
i18n: {
459+
getMessage: (key) => key
460+
}
461+
});
462+
463+
// Load webhooks first
464+
await loadWebhooks();
465+
466+
// Find and click edit button
467+
const editBtn = document.querySelector('.edit-btn');
468+
if (editBtn) {
469+
editBtn.click();
470+
expect(testBtn.classList.contains('hidden')).toBe(false);
471+
}
472+
});
473+
474+
test('test button includes x-webhook-test header', async () => {
475+
const addBtn = document.getElementById('add-new-webhook-btn');
476+
const testBtn = document.getElementById('test-webhook-btn');
477+
const urlInput = document.getElementById('webhook-url');
478+
479+
mockSendWebhook.mockResolvedValue();
480+
481+
// Show the form and set URL
482+
addBtn.click();
483+
urlInput.value = 'https://test.com';
484+
485+
// Click test button
486+
await testBtn.click();
487+
488+
// Verify the test header was added
489+
const calledWith = mockSendWebhook.mock.calls[0][0];
490+
const testHeader = calledWith.headers.find(h => h.key === 'x-webhook-test');
491+
492+
expect(testHeader).toBeDefined();
493+
expect(testHeader.value).toBe('true');
494+
});
495+
496+
test('test button uses real data not test payload', async () => {
497+
const addBtn = document.getElementById('add-new-webhook-btn');
498+
const testBtn = document.getElementById('test-webhook-btn');
499+
const urlInput = document.getElementById('webhook-url');
500+
501+
mockSendWebhook.mockResolvedValue();
502+
503+
// Show the form and set URL
504+
addBtn.click();
505+
urlInput.value = 'https://test.com';
506+
507+
// Click test button
508+
await testBtn.click();
509+
510+
// Verify sendWebhook was called with false (real data, not test payload)
511+
expect(mockSendWebhook).toHaveBeenCalledWith(
512+
expect.any(Object),
513+
false // This should be false for real data
514+
);
515+
});
516+
});
267517
});

0 commit comments

Comments
 (0)