emailTemplateTest.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. 'use strict';
  2. require('../common');
  3. // DOM builder that mirrors bootstrap5.php navbar
  4. function buildEmailDomNoShortUrl() {
  5. $('body').html(
  6. // TopNav expects initially hidden #emaillink BUTTON.
  7. '<nav><div id="navbar"><ul>' +
  8. '<li>' +
  9. '<button id="clonebutton" type="button" class="btn btn-warning navbar-btn">' +
  10. '<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> Clone' +
  11. '</button>' +
  12. '</li>' +
  13. '<li>' +
  14. '<button id="emaillink" type="button" class="hidden btn btn-secondary">Email</button>' +
  15. '</li>' +
  16. '</ul></div></nav>' +
  17. '<input id="burnafterreadingoption" type="checkbox">' +
  18. // include dummy email confirm modal for sendEmail
  19. '<div id="emailconfirmmodal" class="hidden">' +
  20. '<div id="emailconfirm-timezone-current"></div>' +
  21. '<div id="emailconfirm-timezone-utc"></div>' +
  22. '</div>'
  23. );
  24. }
  25. // DOM builder that adds the shortener result block
  26. function buildEmailDomWithShortUrl() {
  27. buildEmailDomNoShortUrl();
  28. $('body').html(
  29. // TopNav expectsinitially hidden #emaillink BUTTON.
  30. '<nav><div id="navbar"><ul>' +
  31. '<li>' +
  32. '<button id="clonebutton" type="button" class="btn btn-warning navbar-btn">' +
  33. '<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> Clone' +
  34. '</button>' +
  35. '</li>' +
  36. '<li>' +
  37. '<button id="emaillink" type="button" class="hidden btn btn-secondary">Email</button>' +
  38. '</li>' +
  39. '</ul></div></nav>' +
  40. '<input id="burnafterreadingoption" type="checkbox">' +
  41. '<div id="pastelink">Your document is ' +
  42. '<a id="pasteurl" href="https://short.example/xYz">https://short.example/xYz</a> ' +
  43. '<span id="copyhint">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>' +
  44. '</div>' +
  45. // add a minimal email confirmation modal so sendEmail does not crash
  46. '<div id="emailconfirmmodal" class="hidden">' +
  47. '<div id="emailconfirm-timezone-current"></div>' +
  48. '<div id="emailconfirm-timezone-utc"></div>' +
  49. '</div>'
  50. );
  51. }
  52. function stubWinOpen($element) {
  53. const win = $element[0].ownerDocument.defaultView;
  54. // Some helpers in privatebin.js expect a global document.
  55. global.document = win.document;
  56. let openedUrl = null;
  57. const origOpen = win.open;
  58. // Prefer simple assignment; if blocked, fall back to defineProperty.
  59. try {
  60. win.open = function (url) {
  61. openedUrl = url;
  62. return {};
  63. };
  64. } catch (e) {
  65. Object.defineProperty(win, 'open', {
  66. value: function (url) {
  67. openedUrl = url;
  68. return {};
  69. },
  70. configurable: true,
  71. writable: true
  72. });
  73. }
  74. return {
  75. getUrl: () => openedUrl,
  76. restore: () => { try { win.open = origOpen; } catch (e) { /* suppress exception in restore */ } },
  77. win
  78. };
  79. }
  80. // Extract and decode the body from a "mailto:?body=..." URL.
  81. function extractMailtoBody(mailtoUrl) {
  82. assert.match(mailtoUrl, /^mailto:\?body=/, 'expected a mailto:?body= URL');
  83. return decodeURIComponent(mailtoUrl.replace(/^mailto:\?body=/, ''));
  84. }
  85. describe('Email - mail body content (short URL vs. fallback)', function () {
  86. before(function () {
  87. cleanup(); // provided by common
  88. });
  89. it('Uses the short URL when #pasteurl is present and never includes "undefined"', function () {
  90. buildEmailDomWithShortUrl(); // with #pastelink/#pasteurl
  91. PrivateBin.TopNav.init();
  92. PrivateBin.TopNav.showEmailButton(0);
  93. const $emailBtn = $('#emaillink');
  94. assert.ok(!$emailBtn.hasClass('hidden'), '#emaillink should be visible after showEmailButton');
  95. const { getUrl, restore } = stubWinOpen($emailBtn);
  96. try {
  97. $emailBtn.trigger('click');
  98. const openedUrl = getUrl();
  99. assert.ok(openedUrl, 'window.open should have been called');
  100. const body = extractMailtoBody(openedUrl);
  101. assert.match(body, /https:\/\/short\.example\/xYz/, 'email body should include the short URL');
  102. assert.doesNotMatch(body, /undefined/, 'email body must not contain "undefined"');
  103. } finally {
  104. restore();
  105. cleanup();
  106. }
  107. });
  108. it('Falls back to window.location.href when #pasteurl is absent and never includes "undefined"', function () {
  109. buildEmailDomNoShortUrl(); // No #pasteurl
  110. PrivateBin.TopNav.init();
  111. PrivateBin.TopNav.showEmailButton(0);
  112. const $emailBtn = $('#emaillink');
  113. assert.ok(!$emailBtn.hasClass('hidden'), '#emaillink should be visible after showEmailButton');
  114. const { getUrl, restore, win } = stubWinOpen($emailBtn);
  115. try {
  116. $emailBtn.trigger('click');
  117. const openedUrl = getUrl();
  118. assert.ok(openedUrl, 'window.open should have been called');
  119. const body = extractMailtoBody(openedUrl);
  120. assert.match(body, new RegExp(win.location.href), 'email body should include the fallback page URL');
  121. assert.doesNotMatch(body, /undefined/, 'email body must not contain "undefined"');
  122. } finally {
  123. restore();
  124. cleanup();
  125. }
  126. });
  127. });