Repository: manusa/electronim
Pull request #28 (2020-07-14 15:52:22)

Features/#3 Disabling notifications
First of all, I'm somewhat stupid, and started committing with the number of the issue 2. This PR is for the feature #3 

Next, I included modifications in the settings window to:

- include icons for enabling/disabling notifications in every tab (respecting the awful design provided by @manusa )
- include a different setting to control globally the notifications of the application

Finally, I also included the changes to retrieve those settings, and:

- For each tab **DO NOT CREATE** a Notification object if notifications are disabled (either globally or for that particular tab)
- Also, sounds were still playing when a message arrived to the service included in the tab, so I muted the entire tab.

visomar: @manusa Do not close #3, since there are things that are not quite clear with this topic:

- The option to select an amount of time for the notifications to be disabled is not included in this PR, but maybe we can create a different issue to hold that scope... your call.
- As we discussed, the Notification object to which the notifications are delegated is not created if notifications are disabled. That provides the functionality described in the issue, but do we have to consider if the notification still has to be created?

What I mean with the last question is, notifications can be silent (they don't disturb you, and still are listed in you notifications bar), so if you want to still check for them, they are there, and you can see what messages you weren't notified of. That was my initial approach, but I couldn't find the way to do it like this. Maybe it is better to create an enhancement about this?
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='90.9%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list) [90.9% Coverage](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='90.9%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list) [90.9% Coverage](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60.png' alt='88.9%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list) [88.9% Coverage](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60.png' alt='88.9%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list) [88.9% Coverage](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60.png' alt='84.4%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list) [84.4% Coverage](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=manusa_electronim&pullRequest=28&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='96.9%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list) [96.9% Coverage](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=manusa_electronim&pullRequest=28&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/package.json b/package.json
index 9fe0acd..1d6c16f 100644
--- a/package.json
+++ b/package.json
@@ -135,7 +135,9 @@
     }
   },
   "jest": {
-    "setupFilesAfterEnv": ["jest-extended"]
+    "setupFilesAfterEnv": [
+      "jest-extended"
+    ]
   },
   "devDependencies": {
     "@testing-library/dom": "^7.20.0",
diff --git a/src/constants/index.js b/src/constants/index.js
index 2cf2d8c..2f30849 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -25,6 +25,7 @@ const APP_EVENTS = {
   settingsSave: 'settingsSave',
   setTabFavicon: 'setTabFavicon',
   setTabTitle: 'setTabTitle',
+  canNotify: 'canNotify',
   tabsReady: 'tabsReady',
   tabReorder: 'tabReorder',
   zoomIn: 'zoomIn',
diff --git a/src/main/index.js b/src/main/index.js
index 4d170b6..8013691 100644
--- a/src/main/index.js
+++ b/src/main/index.js
@@ -16,7 +16,7 @@
 const {BrowserWindow, Notification, app, ipcMain: ipc} = require('electron');
 const {APP_EVENTS} = require('../constants');
 const {TABS_CONTAINER_HEIGHT, initTabContainer} = require('../chrome-tabs');
-const {loadSettings, updateSettings, updateTabUrls, openSettingsDialog} = require('../settings');
+const {loadSettings, updateSettings, openSettingsDialog} = require('../settings');
 const {loadDictionaries} = require('../spell-check');
 const tabManager = require('../tab-manager');
 const {initBrowserVersions, userAgentForView} = require('../user-agent');
@@ -84,6 +84,9 @@ const initTabListener = () => {
     }
   });
   ipc.on(APP_EVENTS.activateTab, (event, data) => activateTab(data.id));
+  ipc.on(APP_EVENTS.canNotify, (event, tabId) => {
+    event.returnValue = tabManager.canNotify(tabId);
+  });
   ipc.on(APP_EVENTS.notificationClick, (event, {tabId}) => {
     tabContainer.webContents.send(APP_EVENTS.activateTabInContainer, {tabId});
     mainWindow.restore();
@@ -104,9 +107,12 @@ const closeSettings = () => {
   settingsView.destroy();
 };
 
-const saveSettings = (event, {tabs, enabledDictionaries}) => {
-  updateSettings({enabledDictionaries: [...enabledDictionaries]});
-  updateTabUrls(tabs);
+const saveSettings = (event, {tabs, enabledDictionaries, disableNotificationsGlobally}) => {
+  let {activeTab} = loadSettings();
+  if (tabs.length > 0 && !tabs.some(tab => tab.id === activeTab)) {
+    activeTab = tabs[0].id;
+  }
+  updateSettings({tabs, enabledDictionaries: [...enabledDictionaries], activeTab, disableNotificationsGlobally});
   loadDictionaries();
   const currentBrowserView = mainWindow.getBrowserView();
   mainWindow.removeBrowserView(currentBrowserView);
@@ -153,6 +159,7 @@ const init = () => {
       }).show();
       browserVersionsReady();
     });
+
   return mainWindow;
 };
 
diff --git a/src/settings/browser-settings.css b/src/settings/browser-settings.css
index 4845301..9b90547 100644
--- a/src/settings/browser-settings.css
+++ b/src/settings/browser-settings.css
@@ -78,6 +78,12 @@ body {
     max-height: 100px;
 }
 
+.settings__global-notifications {
+    display: inline-flex;
+    align-items: center;
+    flex-wrap: wrap;
+}
+
 .settings__dictionaries .control input[type="checkbox"] {
     margin-right: var(--default-spacing);
 }
diff --git a/src/settings/browser-settings.js b/src/settings/browser-settings.js
index 15db31d..77ac445 100644
--- a/src/settings/browser-settings.js
+++ b/src/settings/browser-settings.js
@@ -56,7 +56,8 @@ const initialState = {
   expandedTabs: [],
   newTabValid: false,
   newTabValue: '',
-  canSave: window.tabs.length > 0
+  canSave: window.tabs.length > 0,
+  disableNotificationsGlobally: window.disableNotificationsGlobally
 };
 
 const ACTIONS = {
@@ -65,6 +66,7 @@ const ACTIONS = {
   TOGGLE_DICTIONARY: 'TOGGLE_DICTIONARY',
   TOGGLE_TAB_EXPANDED: 'TOGGLE_TAB_EXPANDED',
   TOGGLE_TAB_PROPERTY: 'TOGGLE_TAB_PROPERTY',
+  TOGGLE_GLOBAL_NOTIFICATIONS: 'TOGGLE_GLOBAL_NOTIFICATIONS',
   UPDATE_NEW_TAB_VALUE: 'UPDATE_NEW_TAB_VALUE'
 };
 
@@ -83,6 +85,7 @@ const reducer = (state, action) => {
         tabs: [...state.tabs, {
           id: newId(),
           sandboxed: false,
+          disableNotifications: false,
           url: prependProtocol(state.newTabValue)
         }],
         canSave: true
@@ -124,6 +127,11 @@ const reducer = (state, action) => {
       });
       return newState;
     }
+    case ACTIONS.TOGGLE_GLOBAL_NOTIFICATIONS: {
+      return {...state,
+        disableNotificationsGlobally: !state.disableNotificationsGlobally
+      };
+    }
     case ACTIONS.UPDATE_NEW_TAB_VALUE: {
       return {...state,
         newTabValid: validateUrl(action.payload),
@@ -174,7 +182,7 @@ const TabAdvancedSettings = ({dispatch, id, disabled = false}) => (html`
   </div>
 `);
 
-const TabEntry = ({dispatch, id, expanded, url, sandboxed, ...tab}) => (html`
+const TabEntry = ({dispatch, id, expanded, url, sandboxed, disableNotifications, ...tab}) => (html`
   <div class='settings__tab ${expanded && 'settings__tab--expanded'} panel-block' data-id=${id}>
     <div class='settings__tab-main'>
       <${ExpandButton} dispatch=${dispatch} id=${id} expanded=${expanded} />
@@ -185,6 +193,10 @@ const TabEntry = ({dispatch, id, expanded, url, sandboxed, ...tab}) => (html`
         title='Use isolated session when lock is on'
         onclick=${toggleTabProperty(dispatch, 'sandboxed', id)}
       />
+      <${SettingsButton} icon=${disableNotifications ? 'fa-bell-slash' : 'fa-bell'}
+        title=${disableNotifications ? 'Notifications disabled. Click to enable' : 'Notifications enabled. Click to disable'}
+        onclick=${toggleTabProperty(dispatch, 'disableNotifications', id)}
+      />
       <${SettingsButton} icon='fa-trash' title='Delete tab'
         onclick=${() => dispatch({type: ACTIONS.REMOVE, payload: {id}})}
       /> 
@@ -216,9 +228,11 @@ const Settings = () => {
       addTab();
     }
   };
+  const toggleNotifications = () => dispatch({type: ACTIONS.TOGGLE_GLOBAL_NOTIFICATIONS});
   const save = () => ipcRenderer.send(APP_EVENTS.settingsSave, {
     tabs: state.tabs,
-    enabledDictionaries
+    enabledDictionaries,
+    disableNotificationsGlobally: state.disableNotificationsGlobally
   });
   const cancel = () => ipcRenderer.send(APP_EVENTS.settingsCancel);
   return html`
@@ -262,11 +276,24 @@ const Settings = () => {
           </div>
         </div>
       </nav>
+      <nav class="panel">
+        <p class="panel-heading">Other settings</p>
+        <div class="panel-block">
+          <div class="settings__global-notifications container">
+            <${Checkbox}
+              label="Disable notifications globally"
+              checked=${state.disableNotificationsGlobally}
+              value=${state.disableNotificationsGlobally}
+              onclick=${toggleNotifications}
+            />
+          </div>
+        </div>
+      </nav>
       <div class="field is-grouped">
         <div class="control">
           <button class="settings__submit button is-link"
             disabled=${!state.canSave} onclick=${save}>Ok</button>
-      </div>
+        </div>
         <div class="control">
           <button class="settings__cancel button is-link is-light" onclick=${cancel}>
             Cancel
diff --git a/src/settings/index.js b/src/settings/index.js
index f5b5379..6ccd5e8 100644
--- a/src/settings/index.js
+++ b/src/settings/index.js
@@ -47,14 +47,6 @@ const writeSettings = settings => {
 
 const updateSettings = settings => writeSettings({...loadSettings(), ...settings});
 
-const updateTabUrls = updatedTabs => {
-  const {activeTab} = loadSettings();
-  updateSettings({tabs: [...updatedTabs]});
-  if (updatedTabs.length > 0 && !updatedTabs.map(({id}) => id).includes(activeTab)) {
-    updateSettings({activeTab: updatedTabs[0].id});
-  }
-};
-
 const openSettingsDialog = mainWindow => {
   const settingsView = new BrowserView({webPreferences});
   mainWindow.setBrowserView(settingsView);
@@ -64,4 +56,4 @@ const openSettingsDialog = mainWindow => {
   settingsView.webContents.loadURL(`file://${__dirname}/index.html`);
 };
 
-module.exports = {loadSettings, updateSettings, updateTabUrls, openSettingsDialog};
+module.exports = {loadSettings, updateSettings, openSettingsDialog};
diff --git a/src/settings/preload.js b/src/settings/preload.js
index 8f6b1a3..b1f0f4a 100644
--- a/src/settings/preload.js
+++ b/src/settings/preload.js
@@ -32,3 +32,5 @@ window.dictionaries = {
 };
 
 window.tabs = [...settings.tabs];
+
+window.disableNotificationsGlobally = settings.disableNotificationsGlobally;
diff --git a/src/tab-manager/browser-notification-shim.js b/src/tab-manager/browser-notification-shim.js
index 20f67dd..bbc7774 100644
--- a/src/tab-manager/browser-notification-shim.js
+++ b/src/tab-manager/browser-notification-shim.js
@@ -24,91 +24,96 @@ const NativeNotification = Notification;
 
 const bubbleNotification = () => ipcRenderer.send(APP_EVENTS.notificationClick, {tabId: window.tabId});
 
+const canNotify = () => ipcRenderer.sendSync(APP_EVENTS.canNotify, window.tabId);
+
 const setDelegateMinimumBehavior = delegate => {
   delegate.onclick = bubbleNotification;
 };
 
 // noinspection JSValidateTypes
 Notification = function() {
-  const delegate = new NativeNotification(...arguments);
-  setDelegateMinimumBehavior(delegate);
-  return {
-    get actions() {
-      return delegate.actions;
-    },
-    get badge() {
-      return delegate.badge;
-    },
-    get body() {
-      return delegate.body;
-    },
-    get data() {
-      return delegate.data;
-    },
-    get dir() {
-      return delegate.dir;
-    },
-    get lang() {
-      return delegate.lang;
-    },
-    get tag() {
-      return delegate.tag;
-    },
-    get icon() {
-      return delegate.icon;
-    },
-    get image() {
-      return delegate.image;
-    },
-    get renotify() {
-      return delegate.renotify;
-    },
-    get requireInteraction() {
-      return delegate.requireInteraction;
-    },
-    get silent() {
-      return delegate.silent;
-    },
-    get timestamp() {
-      return delegate.timestamp;
-    },
-    get title() {
-      return delegate.title;
-    },
-    get vibrate() {
-      return delegate.vibrate;
-    },
-    get onclick() {
-      return delegate.onclick;
-    },
-    set onclick(func) {
-      delegate.onclick = event => {
-        bubbleNotification();
-        func(event);
-      };
-    },
-    get onclose() {
-      return delegate.onclose;
-    },
-    set onclose(func) {
-      delegate.onclose = func;
-    },
-    get onerror() {
-      return delegate.onerror;
-    },
-    set onerror(func) {
-      delegate.onerror = func;
-    },
-    get onshow() {
-      return delegate.onshow;
-    },
-    set onshow(func) {
-      delegate.onshow = func;
-    },
-    get close() {
-      return delegate.close;
-    }
-  };
+  if (canNotify()) {
+    const delegate = new NativeNotification(...arguments);
+    setDelegateMinimumBehavior(delegate);
+    return {
+      get actions() {
+        return delegate.actions;
+      },
+      get badge() {
+        return delegate.badge;
+      },
+      get body() {
+        return delegate.body;
+      },
+      get data() {
+        return delegate.data;
+      },
+      get dir() {
+        return delegate.dir;
+      },
+      get lang() {
+        return delegate.lang;
+      },
+      get tag() {
+        return delegate.tag;
+      },
+      get icon() {
+        return delegate.icon;
+      },
+      get image() {
+        return delegate.image;
+      },
+      get renotify() {
+        return delegate.renotify;
+      },
+      get requireInteraction() {
+        return delegate.requireInteraction;
+      },
+      get silent() {
+        return delegate.silent;
+      },
+      get timestamp() {
+        return delegate.timestamp;
+      },
+      get title() {
+        return delegate.title;
+      },
+      get vibrate() {
+        return delegate.vibrate;
+      },
+      get onclick() {
+        return delegate.onclick;
+      },
+      set onclick(func) {
+        delegate.onclick = event => {
+          bubbleNotification();
+          func(event);
+        };
+      },
+      get onclose() {
+        return delegate.onclose;
+      },
+      set onclose(func) {
+        delegate.onclose = func;
+      },
+      get onerror() {
+        return delegate.onerror;
+      },
+      set onerror(func) {
+        delegate.onerror = func;
+      },
+      get onshow() {
+        return delegate.onshow;
+      },
+      set onshow(func) {
+        delegate.onshow = func;
+      },
+      get close() {
+        return delegate.close;
+      }
+    };
+  }
+  return {};
 };
 
 Notification.maxActions = NativeNotification.maxActions;
diff --git a/src/tab-manager/index.js b/src/tab-manager/index.js
index f0d669b..0c46df9 100644
--- a/src/tab-manager/index.js
+++ b/src/tab-manager/index.js
@@ -119,6 +119,12 @@ const removeAll = () => {
 
 const reload = () => Object.values(tabs).forEach(browserView => browserView.webContents.reload());
 
+const canNotify = tabId => {
+  const {tabs: tabsSettings, disableNotificationsGlobally} = settings.loadSettings();
+  const currentTab = tabsSettings.find(tab => tab.id === tabId);
+  return !(currentTab.disableNotifications || disableNotificationsGlobally);
+};
+
 module.exports = {
-  addTabs, getTab, getActiveTab, setActiveTab, reload, removeAll
+  addTabs, getTab, getActiveTab, setActiveTab, canNotify, reload, removeAll
 };


Test patch:
diff --git a/src/main/__tests__/index.test.js b/src/main/__tests__/index.test.js
index f68c418..17897ae 100644
--- a/src/main/__tests__/index.test.js
+++ b/src/main/__tests__/index.test.js
@@ -192,6 +192,17 @@ describe('Main module test suite', () => {
         expect(mockBrowserWindow.addBrowserView).toHaveBeenCalledBefore(activeTab.setBounds);
       });
     });
+    test('canNotify, should call to the canNotify method of the tabManager', () => {
+      // Given
+      const mockIpcMainEvent = {returnValue: null};
+      tabManagerModule.canNotify = jest.fn(() => 'yepe');
+      main.init();
+      // When
+      mockIpc.listeners.canNotify(mockIpcMainEvent, 'validId');
+      // Then
+      expect(tabManagerModule.canNotify).toHaveBeenCalledWith('validId');
+      expect(mockIpcMainEvent.returnValue).toBe('yepe');
+    });
     test('notificationClick, should restore window and activate tab', () => {
       // Given
       mockTabContainer.webContents = {send: jest.fn()};
@@ -304,14 +315,17 @@ describe('Main module test suite', () => {
     test('saveSettings, should reload settings and reset all views', () => {
       // Given
       mockBrowserWindow.removeBrowserView = jest.fn();
+      mockSettings = {activeTab: 42};
       mockTabContainer.destroy = jest.fn();
       main.init();
       // When
-      mockIpc.listeners.settingsSave({}, {tabs: [], enabledDictionaries: []});
+      mockIpc.listeners.settingsSave({}, {tabs: [{id: 1337}], enabledDictionaries: []});
       // Then
       expect(spellCheckModule.loadDictionaries).toHaveBeenCalledTimes(2);
       expect(settingsModule.updateSettings).toHaveBeenCalledTimes(1);
-      expect(settingsModule.updateTabUrls).toHaveBeenCalledTimes(1);
+      expect(settingsModule.updateSettings).toHaveBeenCalledWith(
+        expect.objectContaining({activeTab: 1337})
+      );
       expect(mockBrowserWindow.removeBrowserView).toHaveBeenCalledTimes(1);
       expect(mockBrowserWindow.removeBrowserView).toHaveBeenCalledWith(settingsView);
       expect(tabManagerModule.removeAll).toHaveBeenCalledTimes(1);
@@ -325,7 +339,6 @@ describe('Main module test suite', () => {
       mockIpc.listeners.settingsCancel();
       // Then
       expect(settingsModule.updateSettings).not.toHaveBeenCalled();
-      expect(settingsModule.updateTabUrls).not.toHaveBeenCalled();
       expect(tabManagerModule.getActiveTab).toHaveBeenCalledTimes(1);
       expect(settingsView.destroy).toHaveBeenCalledTimes(1);
     });
diff --git a/src/settings/__tests__/browser-settings.test.js b/src/settings/__tests__/browser-settings.test.js
index 1e34a82..0b2485e 100644
--- a/src/settings/__tests__/browser-settings.test.js
+++ b/src/settings/__tests__/browser-settings.test.js
@@ -34,7 +34,7 @@ describe('Settings in Browser test suite', () => {
     }, enabled: ['en']};
     mockTabs = [
       {id: '1', url: 'https://initial-tab.com', sandboxed: true},
-      {id: '2', url: 'https://initial-tab-2.com'}
+      {id: '2', url: 'https://initial-tab-2.com', disableNotifications: true}
     ];
     mockIpcRenderer = {
       send: jest.fn()
@@ -47,6 +47,7 @@ describe('Settings in Browser test suite', () => {
     window.dictionaries = mockDictionaries;
     window.tabs = mockTabs;
     window.ipcRenderer = mockIpcRenderer;
+    window.disableNotificationsGlobally = false;
     mockDOM();
     jest.isolateModules(() => {
       require('../browser-settings');
@@ -61,10 +62,14 @@ describe('Settings in Browser test suite', () => {
       // Then
       expect(mockIpcRenderer.send).toHaveBeenCalledTimes(1);
       expect(mockIpcRenderer.send).toHaveBeenCalledWith('Save my settings',
-        {tabs: [
-          {id: '1', url: 'https://initial-tab.com', sandboxed: true},
-          {id: '2', url: 'https://initial-tab-2.com'}
-        ], enabledDictionaries: ['en']});
+        {
+          tabs: [
+            {id: '1', url: 'https://initial-tab.com', sandboxed: true},
+            {id: '2', url: 'https://initial-tab-2.com', disableNotifications: true}
+          ],
+          enabledDictionaries: ['en'],
+          disableNotificationsGlobally: false
+        });
     });
     test('Cancel should send cancel event', () => {
       // Given
@@ -75,6 +80,17 @@ describe('Settings in Browser test suite', () => {
       expect(mockIpcRenderer.send).toHaveBeenCalledTimes(1);
       expect(mockIpcRenderer.send).toHaveBeenCalledWith('Cancel my settings');
     });
+    test('Toggle global notifications should check input', async () => {
+      // Given
+      const $notificationCheckbox = document.querySelector('.settings__global-notifications input');
+      expect($notificationCheckbox.checked).toBe(false);
+      // When
+      fireEvent.click($notificationCheckbox);
+      // Then
+      await waitFor(() => {
+        expect($notificationCheckbox.checked).toBe(true);
+      });
+    });
   });
   describe('New tab Input field', () => {
     let $tabContainer;
@@ -212,6 +228,26 @@ describe('Settings in Browser test suite', () => {
         expect($lockOpenIcon.classList.contains('fa-lock-open')).toBe(false));
       expect($lockOpenIcon.classList.contains('fa-lock')).toBe(true);
     });
+    test('Notification disabled icon click, should enable notification', async () => {
+      // Given
+      const $notificationEnabledIcon = document.querySelector('.settings__tabs .icon .fa-bell-slash');
+      // When
+      fireEvent.click($notificationEnabledIcon);
+      // Then
+      await waitFor(() =>
+        expect($notificationEnabledIcon.classList.contains('fa-bell-slash')).toBe(false));
+      expect($notificationEnabledIcon.classList.contains('fa-bell')).toBe(true);
+    });
+    test('Notification enabled icon click, should disable notification', async () => {
+      // Given
+      const $notificationEnabledIcon = document.querySelector('.settings__tabs .icon .fa-bell');
+      // When
+      fireEvent.click($notificationEnabledIcon);
+      // Then
+      await waitFor(() =>
+        expect($notificationEnabledIcon.classList.contains('fa-bell')).toBe(false));
+      expect($notificationEnabledIcon.classList.contains('fa-bell-slash')).toBe(true);
+    });
     test('Trash icon click, should remove tab', async () => {
       // Given
       const $tabContainer = document.querySelector('.settings__tabs');
diff --git a/src/settings/__tests__/index.test.js b/src/settings/__tests__/index.test.js
index 73a7eb5..3039728 100644
--- a/src/settings/__tests__/index.test.js
+++ b/src/settings/__tests__/index.test.js
@@ -97,37 +97,6 @@ describe('Settings module test suite', () => {
         '  "otherSetting": "1337"\n}');
     });
   });
-  describe('updateTabUrls', () => {
-    test('No tab URLs provided and existing tabs, should save empty array', () => {
-      // Given
-      fs.existsSync.mockImplementation(() => true);
-      fs.readFileSync.mockImplementation(
-        () => '{"enabledDictionaries":[], "tabs": [{"id": "1", "url": "http://to-be-removed"}]}');
-      // When
-      settings.updateTabUrls([]);
-      // Then
-      expect(fs.writeFileSync).toHaveBeenCalledWith(path.join('$HOME', '.electronim', 'settings.json'),
-        '{\n  "tabs": [],\n  "enabledDictionaries": []\n}');
-    });
-    test('New tab URLs provided and existing tabs, should save new tabs', () => {
-      // Given
-      fs.existsSync.mockImplementation(() => true);
-      fs.readFileSync.mockImplementation(
-        () => '{"enabledDictionaries":[], "tabs": [{"id": "1", "url": "http://to-be-kept", "otherSetting": 1337}]}');
-      // When
-      settings.updateTabUrls([
-        {id: '1', url: 'http://to-be-kept', sandboxed: true},
-        {id: '1337', url: 'http://new-tab'}
-      ]);
-      // Then
-      expect(fs.readFileSync).toHaveBeenCalledTimes(3);
-      expect(fs.writeFileSync).toHaveBeenCalledWith(path.join('$HOME', '.electronim', 'settings.json'),
-        '{\n  "tabs": [\n' +
-        '    {\n      "id": "1",\n      "url": "http://to-be-kept",\n      "sandboxed": true\n    },\n' +
-        '    {\n      "id": "1337",\n      "url": "http://new-tab"\n    }\n  ],\n' +
-        '  "enabledDictionaries": []\n}');
-    });
-  });
   describe('openSettingsDialog', () => {
     test('', () => {
       // Given
diff --git a/src/settings/__tests__/preload.test.js b/src/settings/__tests__/preload.test.js
index 8d9d849..ff36c2c 100644
--- a/src/settings/__tests__/preload.test.js
+++ b/src/settings/__tests__/preload.test.js
@@ -20,7 +20,7 @@ describe('Settings Module preload test suite', () => {
     mockSpellCheck = {
       AVAILABLE_DICTIONARIES: 'someAvailableDictionaries', getEnabledDictionaries: jest.fn(() => '1337')
     };
-    mockSettings = {loadSettings: jest.fn(() => ({tabs: ['1337']}))};
+    mockSettings = {loadSettings: jest.fn(() => ({tabs: ['1337'], disableNotificationsGlobally: false}))};
     jest.resetModules();
     jest.mock('../../main/preload', () => {
       global.mainPreloadLoaded = true;
@@ -39,5 +39,6 @@ describe('Settings Module preload test suite', () => {
     expect(window.dictionaries.enabled).toBe('1337');
     expect(mockSettings.loadSettings).toHaveBeenCalledTimes(1);
     expect(window.tabs).toEqual(['1337']);
+    expect(window.disableNotificationsGlobally).toBe(false);
   });
 });
diff --git a/src/tab-manager/__tests__/browser-notification-shim.test.js b/src/tab-manager/__tests__/browser-notification-shim.test.js
index 8535886..9a02ce3 100644
--- a/src/tab-manager/__tests__/browser-notification-shim.test.js
+++ b/src/tab-manager/__tests__/browser-notification-shim.test.js
@@ -1,11 +1,16 @@
 describe('Browser Notification Shim test suite', () => {
   let NativeNotification;
   let electron;
+  let mockedElectron;
   beforeEach(() => {
     jest.resetModules();
-    jest.mock('electron', () => ({
-      ipcRenderer: {send: jest.fn()}
-    }));
+    mockedElectron = {
+      ipcRenderer: {
+        send: jest.fn(),
+        sendSync: jest.fn(() => true)
+      }
+    };
+    jest.mock('electron', () => mockedElectron);
     electron = require('electron');
     NativeNotification = jest.fn();
     NativeNotification.maxActions = jest.fn();
@@ -18,58 +23,72 @@ describe('Browser Notification Shim test suite', () => {
     };
     window.Notification = NativeNotification;
   });
-  test('Native Notification should be shimmed and used as delegate', () => {
-    // Given
-    require('../browser-notification-shim');
-    // When
-    const notification = new Notification();
-    Notification.maxActions();
-    Notification.permission();
-    Notification.requestPermission();
-    // Then
-    expect(window.Notification).not.toBe(NativeNotification);
-    expect(NativeNotification).toHaveBeenCalledTimes(1);
-    expect(NativeNotification.maxActions).toHaveBeenCalledTimes(1);
-    expect(NativeNotification.permission).toHaveBeenCalledTimes(1);
-    expect(NativeNotification.requestPermission).toHaveBeenCalledTimes(1);
-    expect(notification.actions).toBe('Actions');
-    expect(notification.badge).toBe('Badge');
-    expect(notification.body).toBe('Body');
-    expect(notification.data).toBe('Data');
-    expect(notification.dir).toBe('Dir');
-    expect(notification.lang).toBe('Lang');
-    expect(notification.tag).toBe('Tag');
-    expect(notification.icon).toBe('Icon');
-    expect(notification.image).toBe('Image');
-    expect(notification.renotify).toBe('Renotify');
-    expect(notification.requireInteraction).toBe('RequireInteraction');
-    expect(notification.silent).toBe('Silent');
-    expect(notification.timestamp).toBe('Timestamp');
-    expect(notification.title).toBe('Title');
-    expect(notification.vibrate).toBe('Vibrate');
-    notification.close();
-    expect(NativeNotification.prototype.close).toHaveBeenCalledTimes(1);
-  });
-  test('Notification should ALWAYS be clickable', () => {
-    // Given
-    require('../browser-notification-shim');
-    const notification = new Notification();
-    // When
-    notification.onclick(null);
-    // Then
-    expect(electron.ipcRenderer.send).toHaveBeenCalledTimes(1);
-    expect(electron.ipcRenderer.send).toHaveBeenCalledWith('notificationClick', expect.any(Object));
+  describe('Notifications are enabled for the current tab', () => {
+    test('Native Notification should be shimmed and used as delegate', () => {
+      // Given
+      require('../browser-notification-shim');
+      // When
+      const notification = new Notification();
+      Notification.maxActions();
+      Notification.permission();
+      Notification.requestPermission();
+      // Then
+      expect(window.Notification).not.toBe(NativeNotification);
+      expect(NativeNotification).toHaveBeenCalledTimes(1);
+      expect(NativeNotification.maxActions).toHaveBeenCalledTimes(1);
+      expect(NativeNotification.permission).toHaveBeenCalledTimes(1);
+      expect(NativeNotification.requestPermission).toHaveBeenCalledTimes(1);
+      expect(notification.actions).toBe('Actions');
+      expect(notification.badge).toBe('Badge');
+      expect(notification.body).toBe('Body');
+      expect(notification.data).toBe('Data');
+      expect(notification.dir).toBe('Dir');
+      expect(notification.lang).toBe('Lang');
+      expect(notification.tag).toBe('Tag');
+      expect(notification.icon).toBe('Icon');
+      expect(notification.image).toBe('Image');
+      expect(notification.renotify).toBe('Renotify');
+      expect(notification.requireInteraction).toBe('RequireInteraction');
+      expect(notification.silent).toBe('Silent');
+      expect(notification.timestamp).toBe('Timestamp');
+      expect(notification.title).toBe('Title');
+      expect(notification.vibrate).toBe('Vibrate');
+      notification.close();
+      expect(NativeNotification.prototype.close).toHaveBeenCalledTimes(1);
+    });
+    test('Notification should ALWAYS be clickable', () => {
+      // Given
+      require('../browser-notification-shim');
+      const notification = new Notification();
+      // When
+      notification.onclick(null);
+      // Then
+      expect(electron.ipcRenderer.send).toHaveBeenCalledTimes(1);
+      expect(electron.ipcRenderer.send).toHaveBeenCalledWith('notificationClick', expect.any(Object));
+    });
+    test('Notification onclick setter, should add custom behavior', () => {
+      // Given
+      require('../browser-notification-shim');
+      const notification = new Notification();
+      const webAppOnclick = jest.fn();
+      notification.onclick = webAppOnclick;
+      // When
+      notification.onclick(null);
+      // Then
+      expect(webAppOnclick).toHaveBeenCalledTimes(1);
+      expect(electron.ipcRenderer.send).toHaveBeenCalledTimes(1);
+    });
   });
-  test('Notification onclick setter, should add custom behavior', () => {
-    // Given
-    require('../browser-notification-shim');
-    const notification = new Notification();
-    const webAppOnclick = jest.fn();
-    notification.onclick = webAppOnclick;
-    // When
-    notification.onclick(null);
-    // Then
-    expect(webAppOnclick).toHaveBeenCalledTimes(1);
-    expect(electron.ipcRenderer.send).toHaveBeenCalledTimes(1);
+  describe('Notifications for this particular tab are disabled', () => {
+    test('notification should not delegate, and should return empty object', () => {
+      // Given
+      mockedElectron.ipcRenderer.sendSync = jest.fn(() => false);
+      require('../browser-notification-shim');
+      // When
+      const notification = new Notification();
+      // Then
+      expect(notification).toEqual({});
+      expect(NativeNotification).not.toBeCalled();
+    });
   });
 });
diff --git a/src/tab-manager/__tests__/index.test.js b/src/tab-manager/__tests__/index.test.js
index cd82747..240d4ac 100644
--- a/src/tab-manager/__tests__/index.test.js
+++ b/src/tab-manager/__tests__/index.test.js
@@ -19,6 +19,7 @@ describe('Tab Manager module test suite', () => {
   let mockMenuItem;
   let userAgent;
   let tabManager;
+  let mockSettings;
   beforeEach(async () => {
     mockBrowserView = {
       setAutoResize: jest.fn(),
@@ -38,7 +39,14 @@ describe('Tab Manager module test suite', () => {
       MenuItem: jest.fn(() => mockMenuItem),
       session: {fromPartition: jest.fn(() => ({}))}
     }));
-    jest.mock('../../settings');
+    mockSettings = {
+      loadSettings: jest.fn(() => ({
+        tabs: [{id: '1337', disableNotifications: false}],
+        disableNotificationsGlobally: false
+      })),
+      updateSettings: jest.fn()
+    };
+    jest.mock('../../settings', () => mockSettings);
     jest.mock('../../spell-check');
     userAgent = require('../../user-agent');
     tabManager = require('../');
@@ -237,4 +245,45 @@ describe('Tab Manager module test suite', () => {
       expect(mockBrowserView.destroy).toHaveBeenCalledTimes(1);
     });
   });
+  describe('canNotify', () => {
+    test('Global notifications enabled, Notifications for this tab enabled, should return true', () => {
+      // Given
+      // When
+      const result = tabManager.canNotify('1337');
+      // Then
+      expect(result).toBe(true);
+    });
+    test('Global notifications disabled, Notifications for this tab enabled, should return false', () => {
+      // Given
+      mockSettings.loadSettings = jest.fn(() => ({
+        tabs: [{id: '1337', disableNotifications: false}],
+        disableNotificationsGlobally: true
+      }));
+      // When
+      const result = tabManager.canNotify('1337');
+      // Then
+      expect(result).toBe(false);
+    });
+    test('Global notifications enabled, Notifications for this tab disabled, should return false', () => {
+      // Given
+      mockSettings.loadSettings = jest.fn(() => ({
+        tabs: [{id: '1337', disableNotifications: true}],
+        disableNotificationsGlobally: false
+      }));
+      // When
+      const result = tabManager.canNotify('1337');
+      // Then
+      expect(result).toBe(false);
+    });
+    test('Notifications undefined in settings, should return true (Opt-out setting)', () => {
+      // Given
+      mockSettings.loadSettings = jest.fn(() => ({
+        tabs: [{id: '1337'}]
+      }));
+      // When
+      const result = tabManager.canNotify('1337');
+      // Then
+      expect(result).toBe(true);
+    });
+  });
 });