Przeglądaj źródła

test: reproduce stacked e-mail button click handlers

Since the jQuery removal in 5b008ac5, the e-mail button and the timezone
buttons of its confirmation dialog get a new anonymous click handler
every time the button is shown or the dialog is opened. The
removeEventListener calls meant to replace them never match, unlike the
namespaced .off() calls of 2.0.x. Repeated clicks send several e-mails,
possibly with the expiration of an earlier document, and hiding the
button doesn't unbind it.

The window.open mock now also counts calls. New tests in
emailTemplateTest.js:
- "Sends a single e-mail after the confirmation dialog was dismissed"
catches stacked timezone button handlers
- "Sends a single e-mail per click after the button was shown again"
catches stacked e-mail button handlers
- "Uses the expiration of the document the button was last shown for"
catches handlers of an earlier document still firing
- "Sends no e-mail after the button was hidden" catches
hideEmailButton not unbinding the handler
- "Sends exactly one e-mail, regardless of how often the button was
shown or the dialog dismissed" as a property test of the invariant

All six tests fail as expected, the existing e-mail tests pass.
El RIDO 1 tydzień temu
rodzic
commit
ee9eb82c88
1 zmienionych plików z 134 dodań i 0 usunięć
  1. 134 0
      js/test/emailTemplateTest.js

+ 134 - 0
js/test/emailTemplateTest.js

@@ -1,5 +1,6 @@
 'use strict';
 require('../common');
+const fc = require('fast-check');
 
 // DOM builder that mirrors bootstrap5.php navbar
 function buildEmailDomNoShortUrl() {
@@ -54,17 +55,20 @@ function buildEmailDomWithShortUrl() {
 function makeWindowOpenMock() {
     const originalOpen = window.open;
     let openedUrl = null;
+    let openedCount = 0;
     let mockRestoreFn = null;
 
     if (typeof jest !== 'undefined' && typeof jest.spyOn === 'function') {
         const spy = jest.spyOn(window, 'open').mockImplementation((url) => {
             openedUrl = url;
+            openedCount++;
             return {};
         });
         mockRestoreFn = () => spy.mockRestore();
     } else {
         window.open = function (url) {
             openedUrl = url;
+            openedCount++;
             return {};
         };
         mockRestoreFn = () => { window.open = originalOpen; };
@@ -72,6 +76,7 @@ function makeWindowOpenMock() {
 
     return {
         getUrl: () => openedUrl,
+        getCount: () => openedCount,
         restore: () => {
             if (mockRestoreFn) {
                 mockRestoreFn();
@@ -252,3 +257,132 @@ describe('Email - mail subject', function () {
         }
     });
 });
+
+describe('Email - click handlers', function () {
+    beforeEach(function () {
+        cleanup(); // provided by common
+    });
+
+    it('Sends a single e-mail after the confirmation dialog was dismissed', function () {
+        buildEmailDomNoShortUrl();
+        PrivateBin.TopNav.init();
+        PrivateBin.TopNav.showEmailButton(3600);
+
+        const { getCount, restore } = makeWindowOpenMock();
+        try {
+            // open the dialog and dismiss it without choosing a timezone
+            document.getElementById('emaillink').click();
+            assert.strictEqual(getCount(), 0, 'no e-mail before choosing a timezone');
+
+            document.getElementById('emaillink').click();
+            document.getElementById('emailconfirm-timezone-current').click();
+            assert.strictEqual(getCount(), 1, 'exactly one e-mail with the current timezone');
+
+            document.getElementById('emaillink').click();
+            document.getElementById('emailconfirm-timezone-utc').click();
+            assert.strictEqual(getCount(), 2, 'exactly one more e-mail with UTC');
+        } finally {
+            restore();
+        }
+    });
+
+    it('Sends a single e-mail per click after the button was shown again', function () {
+        buildEmailDomNoShortUrl();
+        PrivateBin.TopNav.init();
+        // i.e. sending a second document without reloading the page
+        PrivateBin.TopNav.showEmailButton(0);
+        PrivateBin.TopNav.showEmailButton(0);
+
+        const { getCount, restore } = makeWindowOpenMock();
+        try {
+            document.getElementById('emaillink').click();
+            assert.strictEqual(getCount(), 1, 'exactly one e-mail per click');
+        } finally {
+            restore();
+        }
+    });
+
+    it('Uses the expiration of the document the button was last shown for', function () {
+        buildEmailDomNoShortUrl();
+        PrivateBin.TopNav.init();
+        // first document expires, the second never does
+        PrivateBin.TopNav.showEmailButton(3600);
+        PrivateBin.TopNav.showEmailButton(0);
+
+        const { getUrl, getCount, restore } = makeWindowOpenMock();
+        try {
+            document.getElementById('emaillink').click();
+            assert.strictEqual(getCount(), 1, 'sent directly, without confirmation dialog');
+            assert.doesNotMatch(extractMailtoBody(getUrl()), /expire/i, 'body has no expiration');
+
+            document.getElementById('emailconfirm-timezone-current').click();
+            assert.strictEqual(getCount(), 1, 'no e-mail from a stale confirmation dialog');
+        } finally {
+            restore();
+        }
+    });
+
+    it('Sends no e-mail from a confirmation dialog opened for an earlier document', function () {
+        buildEmailDomNoShortUrl();
+        PrivateBin.TopNav.init();
+        PrivateBin.TopNav.showEmailButton(3600);
+
+        const { getCount, restore } = makeWindowOpenMock();
+        try {
+            // open the dialog, but start a new document instead of choosing
+            document.getElementById('emaillink').click();
+            PrivateBin.TopNav.hideEmailButton();
+            PrivateBin.TopNav.showEmailButton(0);
+
+            document.getElementById('emailconfirm-timezone-current').click();
+            document.getElementById('emailconfirm-timezone-utc').click();
+            assert.strictEqual(getCount(), 0, 'no e-mail from a stale confirmation dialog');
+        } finally {
+            restore();
+        }
+    });
+
+    it('Sends no e-mail after the button was hidden', function () {
+        buildEmailDomNoShortUrl();
+        PrivateBin.TopNav.init();
+        PrivateBin.TopNav.showEmailButton(0);
+        PrivateBin.TopNav.hideEmailButton();
+
+        const { getCount, restore } = makeWindowOpenMock();
+        try {
+            // i.e. a keyboard shortcut or assistive technology activating it
+            document.getElementById('emaillink').click();
+            assert.strictEqual(getCount(), 0, 'no e-mail from a hidden button');
+        } finally {
+            restore();
+        }
+    });
+
+    it('Sends exactly one e-mail, regardless of how often the button was shown or the dialog dismissed', function () {
+        fc.assert(fc.property(
+            fc.integer({min: 1, max: 5}),
+            fc.integer({min: 0, max: 5}),
+            fc.constantFrom('current', 'utc'),
+            function (shown, dismissed, timezone) {
+                cleanup();
+                buildEmailDomNoShortUrl();
+                PrivateBin.TopNav.init();
+                for (let i = 0; i < shown; ++i) {
+                    PrivateBin.TopNav.showEmailButton(3600);
+                }
+
+                const { getCount, restore } = makeWindowOpenMock();
+                try {
+                    for (let i = 0; i < dismissed; ++i) {
+                        document.getElementById('emaillink').click();
+                    }
+                    document.getElementById('emaillink').click();
+                    document.getElementById('emailconfirm-timezone-' + timezone).click();
+                    assert.strictEqual(getCount(), 1);
+                } finally {
+                    restore();
+                }
+            }
+        ));
+    });
+});