rubyforgood/casa

View on GitHub
app/javascript/__tests__/notifier.test.js

Summary

Maintainability
D
1 day
Test Coverage
/* eslint-env jest */
import { escape } from 'lodash'

require('jest')
const { Notifier, Notification } = require('../src/notifier.js')

let notificationsElement
let notifier

beforeEach(() => {
  document.body.innerHTML = `<div id="notifications">
  <div class="messages">
  </div>
  <div id="async-waiting-indicator" style="display: none">
    Saving <i class="load-spinner"></i>
  </div>
  <div id="async-success-indicator" class="success-notification" style="display: none">
    Saved
  </div>
  <button id="toggle-minimize-notifications" style="display: none;">
    <span>minimize notifications </span>
    <span class="badge rounded-pill bg-success" style="display: none;"></span>
    <span class="badge rounded-pill bg-warning" style="display: none;"></span>
    <span class="badge rounded-pill bg-danger" style="display: none;"></span>
    <i class="fa-solid fa-minus"></i>
  </button>
</div>`

  $(() => { // JQuery's callback for the DOM loading
    notificationsElement = $('#notifications')
    notifier = new Notifier(notificationsElement)
  })
})

describe('Notifier', () => {
  describe('clicking the minify button', () => {
    let minimizeButton

    beforeEach(() => { // Create a notification so the minify button displays
      $(() => {
        notifier.notify('a notification', 'info')
        minimizeButton = notificationsElement.find('#toggle-minimize-notifications')
      })
    })

    test('should toggle the notifier between the minified and expanded state', (done) => {
      $(() => {
        try {
          const messageNotificationsContainer = notificationsElement.find('.messages')
          const minimizeButtonIcon = minimizeButton.children('i')
          const minimizeButtonText = minimizeButton.children('span').first()

          expect(minimizeButton.css('display')).not.toBe('none')
          expect(messageNotificationsContainer.css('display')).not.toBe('none')
          expect(minimizeButtonIcon.hasClass('fa-minus')).toBeTruthy()
          expect(minimizeButtonIcon.hasClass('fa-plus')).not.toBeTruthy()
          expect(minimizeButtonText.css('display')).not.toBe('none')

          minimizeButton.click()

          expect(messageNotificationsContainer.css('display')).toBe('none')
          expect(minimizeButtonIcon.hasClass('fa-minus')).not.toBeTruthy()
          expect(minimizeButtonIcon.hasClass('fa-plus')).toBeTruthy()
          expect(minimizeButtonText.css('display')).toBe('none')

          minimizeButton.click()

          expect(messageNotificationsContainer.css('display')).not.toBe('none')
          expect(minimizeButtonIcon.hasClass('fa-minus')).toBeTruthy()
          expect(minimizeButtonIcon.hasClass('fa-plus')).not.toBeTruthy()
          expect(minimizeButtonText.css('display')).not.toBe('none')

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('should show only badges where there exists at least one notification matching the badge level when minimized', (done) => {
      $(() => {
        try {
          const minimizeButtonBadgeError = minimizeButton.children('.bg-danger')
          const minimizeButtonBadgeInfo = minimizeButton.children('.bg-success')
          const minimizeButtonBadgeWarning = minimizeButton.children('.bg-warning')

          expect(minimizeButton.css('display')).not.toBe('none')

          minimizeButton.click()

          expect(minimizeButtonBadgeInfo.css('display')).not.toContain('none')
          expect(minimizeButtonBadgeInfo.text()).toBe('1')
          expect(minimizeButtonBadgeError.css('display')).toContain('none')
          expect(minimizeButtonBadgeWarning.css('display')).toContain('none')

          minimizeButton.click()
          const notification2 = notifier.notify('msg', 'error')
          notifier.notify('msg', 'warn')

          minimizeButton.click()
          expect(minimizeButtonBadgeInfo.css('display')).not.toContain('none')
          expect(minimizeButtonBadgeInfo.text()).toBe('1')
          expect(minimizeButtonBadgeError.css('display')).not.toContain('none')
          expect(minimizeButtonBadgeError.text()).toBe('1')
          expect(minimizeButtonBadgeWarning.css('display')).not.toContain('none')
          expect(minimizeButtonBadgeWarning.text()).toBe('1')

          minimizeButton.click()
          notification2.dismiss()

          minimizeButton.click()
          expect(minimizeButtonBadgeInfo.css('display')).not.toContain('none')
          expect(minimizeButtonBadgeInfo.text()).toBe('1')
          expect(minimizeButtonBadgeError.css('display')).toContain('none')
          expect(minimizeButtonBadgeWarning.css('display')).not.toContain('none')
          expect(minimizeButtonBadgeWarning.text()).toBe('1')

          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('notify', () => {
    test("displays a green notification when passed a message and level='info'", (done) => {
      const notificationMessage = "'Y$deH[|%ROii]jy"

      $(() => {
        try {
          notifier.notify(notificationMessage, 'info')

          const successMessages = notificationsElement.children('.messages').find('.success-notification')

          expect(successMessages.length).toBe(1)
          expect(successMessages[0].innerHTML).toContain(notificationMessage)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test("displays a red notification when passed a message and level='error'", (done) => {
      const notificationMessage = '\\+!h0bbH"yN7dx9.'

      $(() => {
        try {
          notifier.notify(notificationMessage, 'error')

          const failureMessages = notificationsElement.find('.danger-notification')

          expect(failureMessages.length).toBe(1)
          expect(failureMessages[0].innerHTML).toContain(notificationMessage)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('displays the minimize button after no notifications were present before', (done) => {
      $(() => {
        try {
          const messageNotificationsContainer = notificationsElement.find('.messages')
          const minimizeButton = notificationsElement.find('#toggle-minimize-notifications')

          expect(minimizeButton.css('display')).toBe('none')
          expect(messageNotificationsContainer.children().length).toBe(0)

          notifier.notify('msg', 'info')

          expect(minimizeButton.css('display')).not.toBe('none')
          expect(messageNotificationsContainer.children().length).toBeGreaterThan(0)

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    describe('when the notifier is minimized', () => {
      let minimizeButton

      beforeEach(() => {
        $(() => {
          minimizeButton = notificationsElement.find('#toggle-minimize-notifications')

          notifier.notify('msg', 'info')
          minimizeButton.click()
        })
      })

      test("un-hides the badge corresponding to the notification's level if it is the only notification with that level", (done) => {
        $(() => {
          try {
            const minimizeButtonBadgeError = notificationsElement.find('#toggle-minimize-notifications .bg-danger')

            expect(notificationsElement.children('.messages').css('display')).toBe('none')
            expect(minimizeButton.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeError.css('display')).toBe('none')

            notifier.notify('msg', 'error')

            expect(minimizeButtonBadgeError.css('display')).not.toBe('none')

            done()
          } catch (error) {
            done(error)
          }
        })
      })

      test("increments the number on badge corresponding to the notification's level when the badge is already displayed", (done) => {
        $(() => {
          try {
            expect(notificationsElement.children('.messages').css('display')).toBe('none')
            expect(minimizeButton.css('display')).not.toBe('none')

            const minimizeButtonBadgeInfo = notificationsElement.find('#toggle-minimize-notifications .bg-success')

            expect(minimizeButtonBadgeInfo.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeInfo.text()).toBe('1')

            notifier.notify('msg', 'info')

            expect(minimizeButtonBadgeInfo.text()).toBe('2')

            const minimizeButtonBadgeError = notificationsElement.find('#toggle-minimize-notifications .bg-danger')
            notifier.notify('msg', 'error')

            expect(minimizeButtonBadgeError.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeError.text()).toBe('1')

            notifier.notify('msg', 'error')

            expect(minimizeButtonBadgeError.text()).toBe('2')

            const minimizeButtonBadgeWarning = notificationsElement.find('#toggle-minimize-notifications .bg-warning')
            notifier.notify('msg', 'warn')

            expect(minimizeButtonBadgeWarning.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeWarning.text()).toBe('1')

            notifier.notify('msg', 'warn')

            expect(minimizeButtonBadgeWarning.text()).toBe('2')
            done()
          } catch (error) {
            done(error)
          }
        })
      })
    })

    test('appends a dismissable message to the notifications widget', (done) => {
      $(() => {
        try {
          notifier.notify('', 'error')
          notifier.notify('', 'info')

          const messagesContainer = notificationsElement.children('.messages')
          let failureMessages = messagesContainer.find('.danger-notification')
          let successMessages = messagesContainer.find('.success-notification')

          expect(failureMessages.length).toBe(1)
          expect(successMessages.length).toBe(1)

          failureMessages.children('button').click()
          failureMessages = notificationsElement.find('.danger-notification')

          expect(failureMessages.length).toBe(0)

          $(successMessages[0]).children('button').click()
          successMessages = notificationsElement.find('.success-notification')

          expect(successMessages.length).toBe(1)

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('appends a non dismissable message to the notifications widget when message dismissable is turned off', (done) => {
      $(() => {
        try {
          notifier.notify('', 'error', false)

          let failureMessages = notificationsElement.find('.danger-notification')

          expect(failureMessages.length).toBe(1)

          failureMessages.children('button').click()
          failureMessages = notificationsElement.find('.danger-notification')

          expect(failureMessages.length).toBe(1)

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws a RangeError when passed an unsupported message level', (done) => {
      $(() => {
        try {
          expect(() => {
            notifier.notify('message', 'unsupported level')
          }).toThrow(RangeError)

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws a TypeError when param message is not a string', (done) => {
      $(() => {
        try {
          expect(() => {
            notifier.notify(6, 'info')
          }).toThrow(TypeError)

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('returns a Notification object representing the new notification', (done) => {
      $(() => {
        try {
          const notification = notifier.notify('test', 'info')
          const onlyNotification = notificationsElement.children('.messages').children('.success-notification')
          expect(notification.notificationElement.is(onlyNotification)).toBe(true)

          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('resolveAsyncOperation', () => {
    test('displays the saved toast for 2 seconds when not passed an error', (done) => {
      $(() => {
        const savedToast = $('#async-success-indicator')

        try {
          notifier.waitForAsyncOperation()
          expect(savedToast.css('display')).toBe('none')

          setTimeout(() => {
            expect(savedToast.attr('style')).toEqual(expect.not.stringContaining('display: none'))
            done()
          }, 2000)

          notifier.resolveAsyncOperation()
          expect(savedToast.attr('style')).toEqual(expect.not.stringContaining('display: none'))
        } catch (error) {
          done(error)
        }
      })
    })

    test('displays the saved toast for 2 seconds after the last call in a quick succession of calls when not passed an error', (done) => {
      $(() => {
        const savedToast = $('#async-success-indicator')

        try {
          notifier.waitForAsyncOperation()
          notifier.waitForAsyncOperation()
          notifier.waitForAsyncOperation()
          expect(savedToast.css('display')).toBe('none')

          setTimeout(() => {
            expect(savedToast.attr('style')).toEqual(expect.not.stringContaining('display: none'))
            done()
          }, 4000)

          notifier.resolveAsyncOperation()
          expect(savedToast.attr('style')).toEqual(expect.not.stringContaining('display: none'))

          // call resolveAsyncOperation before the previous resolveAsyncOperation call dismisses the saved Toast
          setTimeout(() => {
            notifier.resolveAsyncOperation()
          }, 1000)

          setTimeout(() => {
            notifier.resolveAsyncOperation()
          }, 2000)
        } catch (error) {
          done(error)
        }
      })
    })

    test('displays a red notification when passed an error', (done) => {
      const errorMessage = 'hxDEe@no$~Bl%m^]'

      $(() => {
        try {
          notifier.waitForAsyncOperation()
          notifier.resolveAsyncOperation(errorMessage)

          const failureMessages = notificationsElement.find('.danger-notification')

          expect(failureMessages.length).toBe(1)
          expect(failureMessages[0].innerHTML).toContain(errorMessage)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('hides the loading toast when there are no more async operations to wait on', (done) => {
      $(() => {
        const loadingToast = $('#async-waiting-indicator')

        try {
          notifier.waitForAsyncOperation()
          notifier.waitForAsyncOperation()
          expect(loadingToast.attr('style')).toEqual(expect.not.stringContaining('display: none'))

          notifier.resolveAsyncOperation()
          notifier.resolveAsyncOperation()

          expect(loadingToast.css('display')).toBe('none')

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws an error and display it in a red notification when trying to stop an async operation when it\'s sexpecting to resolve none', (done) => {
      $(() => {
        try {
          expect(() => {
            notifier.resolveAsyncOperation()
          }).toThrow()

          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('totalNotificationCount', () => {
    it('returns the number of notifications the notifier currently has displayed', (done) => {
      $(() => {
        try {
          expect(notifier.totalNotificationCount()).toBe(0)

          const notificationCount = Math.floor(Math.random() * 10) + 1
          const notifications = []

          for (let i = 0; i < notificationCount; i++) {
            notifications.push(notifier.notify('message', 'error'))
          }

          expect(notifier.totalNotificationCount()).toBe(notificationCount)

          const aboutHalfNotificationCount = Math.floor(notificationCount / 2)

          for (let i = 0; i < aboutHalfNotificationCount; i++) {
            notifications.pop().dismiss()
          }

          expect(notifier.totalNotificationCount()).toBe(notificationCount - aboutHalfNotificationCount)

          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('waitForAsyncOperation', () => {
    test('displays the loading indicator', (done) => {
      $(() => {
        const loadingToast = $('#async-waiting-indicator')

        try {
          expect(loadingToast.css('display')).toBe('none')

          notifier.waitForAsyncOperation()
          expect(loadingToast.attr('style')).toEqual(expect.not.stringContaining('display: none'))

          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })
})

describe('Notification', () => {
  let notification
  const notificationDefaultMessage = 'm*GV}.n?@D\\~]jW=JD$d'

  beforeEach(() => {
    $(() => {
      notification = notifier.notify(notificationDefaultMessage, 'warn')
    })
  })

  describe('constructor', () => {
    test('throws a TypeError when passed a non jQuery object', (done) => {
      $(() => {
        try {
          expect(() => {
            // eslint-disable-next-line no-new
            new Notification(3)
          }).toThrow(TypeError)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws a ReferenceError when passed jQuery object not representing anything in the dom', (done) => {
      $(() => {
        try {
          expect(() => {
            // eslint-disable-next-line no-new
            new Notification($('#non-existant-element'))
          }).toThrow(ReferenceError)
          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('dismiss', () => {
    test('removes the notification elements', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)

          notification.dismiss()
          expect(notificationsElement[0].innerHTML).not.toContain(notificationDefaultMessage)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('should throw an error if the notification has already been dismissed', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)

          notification.dismiss()
          expect(notificationsElement[0].innerHTML).not.toContain(notificationDefaultMessage)

          expect(() => {
            notification.dismiss()
          }).toThrow(ReferenceError)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('causes the notifier to hide the minimize button if it dismisses the last notification', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)
          expect($('#toggle-minimize-notifications').css('display')).not.toContain('none')

          notification.dismiss()
          expect(notificationsElement[0].innerHTML).not.toContain(notificationDefaultMessage)
          expect($('#toggle-minimize-notifications').css('display')).toContain('none')
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('hides the minimize button if no notifications are left', (done) => {
      $(() => {
        try {
          const messageNotificationsContainer = notificationsElement.find('.messages')
          const minimizeButton = notificationsElement.find('#toggle-minimize-notifications')

          expect(minimizeButton.css('display')).not.toBe('none')
          expect(messageNotificationsContainer.children().length).toBe(1)

          notification.dismiss()

          expect(minimizeButton.css('display')).toBe('none')
          expect(messageNotificationsContainer.children().length).toBe(0)

          done()
        } catch (error) {
          done(error)
        }
      })
    })

    describe('when the parent notifier is minimized', () => {
      let minimizeButton

      beforeEach(() => {
        $(() => {
          minimizeButton = notificationsElement.find('#toggle-minimize-notifications')

          minimizeButton.click()
        })
      })

      test("hides the badge corresponding to the notification's level when there are no more notifications matching the dismissed notification's level", (done) => {
        $(() => {
          try {
            const minimizeButtonBadgeError = notificationsElement.find('#toggle-minimize-notifications .bg-danger')

            const errorNotification = notifier.notify('msg', 'error')

            expect(notificationsElement.children('.messages').css('display')).toBe('none')
            expect(minimizeButton.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeError.css('display')).not.toBe('none')

            errorNotification.dismiss()

            expect(minimizeButtonBadgeError.css('display')).toBe('none')

            done()
          } catch (error) {
            done(error)
          }
        })
      })

      test("decrements the number on badge corresponding to the notification's level when there are still notifications matching the dismissed notification's level left", (done) => {
        $(() => {
          try {
            expect(notificationsElement.children('.messages').css('display')).toBe('none')
            expect(minimizeButton.css('display')).not.toBe('none')

            const minimizeButtonBadgeInfo = notificationsElement.find('#toggle-minimize-notifications .bg-success')
            const infoNotification = notifier.notify('msg', 'info')
            notifier.notify('msg', 'info')

            expect(minimizeButtonBadgeInfo.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeInfo.text()).toBe('2')

            infoNotification.dismiss()

            expect(minimizeButtonBadgeInfo.text()).toBe('1')

            const minimizeButtonBadgeError = notificationsElement.find('#toggle-minimize-notifications .bg-danger')
            const errorNotification = notifier.notify('msg', 'error')
            notifier.notify('msg', 'error')

            expect(minimizeButtonBadgeError.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeError.text()).toBe('2')

            errorNotification.dismiss()

            expect(minimizeButtonBadgeError.text()).toBe('1')

            const minimizeButtonBadgeWarning = notificationsElement.find('#toggle-minimize-notifications .bg-warning')
            const warningNotification = notifier.notify('msg', 'warn')

            expect(minimizeButtonBadgeWarning.css('display')).not.toBe('none')
            expect(minimizeButtonBadgeWarning.text()).toBe('2')

            warningNotification.dismiss()

            expect(minimizeButtonBadgeWarning.text()).toBe('1')
            done()
          } catch (error) {
            done(error)
          }
        })
      })
    })
  })

  describe('getText', () => {
    test('should return the text of the notification', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)
          expect(notification.getText()).toBe(notificationDefaultMessage)

          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('isUserDismissable', () => {
    test('returns a truthy value if there is a dismiss button, false otherwise', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)
          expect(notification.isUserDismissable()).toBeTruthy()

          notification.notificationElement.children('button').remove()

          expect(notification.isUserDismissable()).not.toBeTruthy()
          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('isDismissed', () => {
    test('returns a falsy value if the notification could be found as a child of the notificatons component', (done) => {
      $(() => {
        try {
          expect($(document).find(notification.notificationElement).length).toBe(1)
          expect(notification.isDismissed()).not.toBeTruthy()
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('returns a truthy value if the notification could not be found as a child of the notificatons component', (done) => {
      $(() => {
        try {
          expect($(document).find(notification.notificationElement).length).toBe(1)

          notification.notificationElement.remove()

          expect($(document).find(notification.notificationElement).length).toBe(0)
          expect(notification.isDismissed()).toBeTruthy()
          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('setUserDismissable', () => {
    test('adds a dismiss button that removes the notification element when clicked if one is not present', (done) => {
      $(() => {
        try {
          const notificationMessage = 'mn6#:6C^*hnQ/:cC;2mM'
          notification = notifier.notify(notificationMessage, 'info', false)

          expect(notificationsElement[0].innerHTML).toContain(notificationMessage)
          expect(notification.notificationElement.children('button').length).toBe(0)

          notification.setUserDismissable(true)

          expect(notification.notificationElement.children('button').length).toBe(1)

          notification.notificationElement.children('button').click()

          expect($(document).find(notification.notificationElement).length).toBe(0)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('does nothing if the notification is already in the desired state', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)
          expect(notification.notificationElement.children('button').length).toBe(1)

          notification.setUserDismissable(true)

          expect(notification.notificationElement.children('button').length).toBe(1)

          const notificationMessage = 'fd@4g*G@.6sV{!^Yj*TR'
          notification = notifier.notify(notificationMessage, 'info', false)

          expect(notificationsElement[0].innerHTML).toContain(notificationMessage)
          expect(notification.notificationElement.children('button').length).toBe(0)

          notification.setUserDismissable(false)

          expect(notification.notificationElement.children('button').length).toBe(0)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('removes the dismiss button that removes the notification element when clicked if the button is present', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)
          expect(notification.notificationElement.children('button').length).toBe(1)

          notification.setUserDismissable(false)

          expect(notification.notificationElement.children('button').length).toBe(0)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws an error if the notification is dismissed', (done) => {
      $(() => {
        try {
          notification.notificationElement.remove()
          expect(notificationsElement[0].innerHTML).not.toContain(notificationDefaultMessage)

          expect(() => {
            notification.setUserDismissable(true)
          }).toThrow(ReferenceError)
          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('setText', () => {
    test('changes the text of the notification', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(notificationDefaultMessage)

          const newNotificationMessage = 'VOr%%:#Vc*tbNbM}iUT}'

          notification.setText(newNotificationMessage)

          expect(notification.notificationElement.text()).toContain(newNotificationMessage)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws an error if the notification has been dismissed', (done) => {
      $(() => {
        try {
          notification.notificationElement.remove()
          expect(notificationsElement[0].innerHTML).not.toContain(notificationDefaultMessage)

          expect(() => {
            notification.setText('new Text')
          }).toThrow(ReferenceError)
          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })

  describe('toggleUserDismissable', () => {
    test('will add a functioning dismiss button for the user if there is none', (done) => {
      $(() => {
        try {
          const notificationMessage = 'nm8j$<w*HszPHkObK._n'
          notification = notifier.notify(notificationMessage, 'info', false)

          expect(notificationsElement[0].innerHTML).toContain(escape(notificationMessage))
          expect(notification.notificationElement.children('button').length).toBe(0)

          notification.toggleUserDismissable()

          expect(notification.notificationElement.children('button').length).toBe(1)

          notification.notificationElement.children('button').click()

          expect($(document).find(notification.notificationElement).length).toBe(0)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('will remove the dismiss button for the user if is present', (done) => {
      $(() => {
        try {
          expect(notificationsElement[0].innerHTML).toContain(escape(notificationDefaultMessage))
          expect(notification.notificationElement.children('button').length).toBe(1)

          notification.toggleUserDismissable()

          expect(notification.notificationElement.children('button').length).toBe(0)
          done()
        } catch (error) {
          done(error)
        }
      })
    })

    test('throws an error if the notification has been dismissed', (done) => {
      $(() => {
        try {
          notification.notificationElement.remove()
          expect(notificationsElement[0].innerHTML).not.toContain(escape(notificationDefaultMessage))

          expect(() => {
            notification.toggleUserDismissable()
          }).toThrow(ReferenceError)
          done()
        } catch (error) {
          done(error)
        }
      })
    })
  })
})