@@ -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