api-dialog-spec.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import { expect } from 'chai';
  2. import { dialog, BrowserWindow } from 'electron/main';
  3. import { closeAllWindows } from './lib/window-helpers';
  4. import { ifit } from './lib/spec-helpers';
  5. import { setTimeout } from 'node:timers/promises';
  6. describe('dialog module', () => {
  7. describe('showOpenDialog', () => {
  8. afterEach(closeAllWindows);
  9. ifit(process.platform !== 'win32')('should not throw for valid cases', () => {
  10. expect(() => {
  11. dialog.showOpenDialog({ title: 'i am title' });
  12. }).to.not.throw();
  13. expect(() => {
  14. const w = new BrowserWindow();
  15. dialog.showOpenDialog(w, { title: 'i am title' });
  16. }).to.not.throw();
  17. });
  18. it('throws errors when the options are invalid', () => {
  19. expect(() => {
  20. dialog.showOpenDialog({ properties: false as any });
  21. }).to.throw(/Properties must be an array/);
  22. expect(() => {
  23. dialog.showOpenDialog({ title: 300 as any });
  24. }).to.throw(/Title must be a string/);
  25. expect(() => {
  26. dialog.showOpenDialog({ buttonLabel: [] as any });
  27. }).to.throw(/Button label must be a string/);
  28. expect(() => {
  29. dialog.showOpenDialog({ defaultPath: {} as any });
  30. }).to.throw(/Default path must be a string/);
  31. expect(() => {
  32. dialog.showOpenDialog({ message: {} as any });
  33. }).to.throw(/Message must be a string/);
  34. });
  35. });
  36. describe('showSaveDialog', () => {
  37. afterEach(closeAllWindows);
  38. ifit(process.platform !== 'win32')('should not throw for valid cases', () => {
  39. expect(() => {
  40. dialog.showSaveDialog({ title: 'i am title' });
  41. }).to.not.throw();
  42. expect(() => {
  43. const w = new BrowserWindow();
  44. dialog.showSaveDialog(w, { title: 'i am title' });
  45. }).to.not.throw();
  46. });
  47. it('throws errors when the options are invalid', () => {
  48. expect(() => {
  49. dialog.showSaveDialog({ title: 300 as any });
  50. }).to.throw(/Title must be a string/);
  51. expect(() => {
  52. dialog.showSaveDialog({ buttonLabel: [] as any });
  53. }).to.throw(/Button label must be a string/);
  54. expect(() => {
  55. dialog.showSaveDialog({ defaultPath: {} as any });
  56. }).to.throw(/Default path must be a string/);
  57. expect(() => {
  58. dialog.showSaveDialog({ message: {} as any });
  59. }).to.throw(/Message must be a string/);
  60. expect(() => {
  61. dialog.showSaveDialog({ nameFieldLabel: {} as any });
  62. }).to.throw(/Name field label must be a string/);
  63. });
  64. });
  65. describe('showMessageBox', () => {
  66. afterEach(closeAllWindows);
  67. // parentless message boxes are synchronous on macOS
  68. // dangling message boxes on windows cause a DCHECK: https://source.chromium.org/chromium/chromium/src/+/main:base/win/message_window.cc;drc=7faa4bf236a866d007dc5672c9ce42660e67a6a6;l=68
  69. ifit(process.platform !== 'darwin' && process.platform !== 'win32')('should not throw for a parentless message box', () => {
  70. expect(() => {
  71. dialog.showMessageBox({ message: 'i am message' });
  72. }).to.not.throw();
  73. });
  74. ifit(process.platform !== 'win32')('should not throw for valid cases', () => {
  75. expect(() => {
  76. const w = new BrowserWindow();
  77. dialog.showMessageBox(w, { message: 'i am message' });
  78. }).to.not.throw();
  79. });
  80. it('throws errors when the options are invalid', () => {
  81. expect(() => {
  82. dialog.showMessageBox(undefined as any, { type: 'not-a-valid-type' as any, message: '' });
  83. }).to.throw(/Invalid message box type/);
  84. expect(() => {
  85. dialog.showMessageBox(null as any, { buttons: false as any, message: '' });
  86. }).to.throw(/Buttons must be an array/);
  87. expect(() => {
  88. dialog.showMessageBox({ title: 300 as any, message: '' });
  89. }).to.throw(/Title must be a string/);
  90. expect(() => {
  91. dialog.showMessageBox({ message: [] as any });
  92. }).to.throw(/Message must be a string/);
  93. expect(() => {
  94. dialog.showMessageBox({ detail: 3.14 as any, message: '' });
  95. }).to.throw(/Detail must be a string/);
  96. expect(() => {
  97. dialog.showMessageBox({ checkboxLabel: false as any, message: '' });
  98. }).to.throw(/checkboxLabel must be a string/);
  99. });
  100. });
  101. describe('showMessageBox with signal', () => {
  102. afterEach(closeAllWindows);
  103. it('closes message box immediately', async () => {
  104. const controller = new AbortController();
  105. const signal = controller.signal;
  106. const w = new BrowserWindow();
  107. const p = dialog.showMessageBox(w, { signal, message: 'i am message' });
  108. controller.abort();
  109. const result = await p;
  110. expect(result.response).to.equal(0);
  111. });
  112. it('closes message box after a while', async () => {
  113. const controller = new AbortController();
  114. const signal = controller.signal;
  115. const w = new BrowserWindow();
  116. const p = dialog.showMessageBox(w, { signal, message: 'i am message' });
  117. await setTimeout(500);
  118. controller.abort();
  119. const result = await p;
  120. expect(result.response).to.equal(0);
  121. });
  122. it('cancels message box', async () => {
  123. const controller = new AbortController();
  124. const signal = controller.signal;
  125. const w = new BrowserWindow();
  126. const p = dialog.showMessageBox(w, {
  127. signal,
  128. message: 'i am message',
  129. buttons: ['OK', 'Cancel'],
  130. cancelId: 1
  131. });
  132. controller.abort();
  133. const result = await p;
  134. expect(result.response).to.equal(1);
  135. });
  136. it('cancels message box after a while', async () => {
  137. const controller = new AbortController();
  138. const signal = controller.signal;
  139. const w = new BrowserWindow();
  140. const p = dialog.showMessageBox(w, {
  141. signal,
  142. message: 'i am message',
  143. buttons: ['OK', 'Cancel'],
  144. cancelId: 1
  145. });
  146. await setTimeout(500);
  147. controller.abort();
  148. const result = await p;
  149. expect(result.response).to.equal(1);
  150. });
  151. });
  152. describe('showErrorBox', () => {
  153. it('throws errors when the options are invalid', () => {
  154. expect(() => {
  155. (dialog.showErrorBox as any)();
  156. }).to.throw(/Insufficient number of arguments/);
  157. expect(() => {
  158. dialog.showErrorBox(3 as any, 'four');
  159. }).to.throw(/Error processing argument at index 0/);
  160. expect(() => {
  161. dialog.showErrorBox('three', 4 as any);
  162. }).to.throw(/Error processing argument at index 1/);
  163. });
  164. });
  165. describe('showCertificateTrustDialog', () => {
  166. it('throws errors when the options are invalid', () => {
  167. expect(() => {
  168. (dialog.showCertificateTrustDialog as any)();
  169. }).to.throw(/options must be an object/);
  170. expect(() => {
  171. dialog.showCertificateTrustDialog({} as any);
  172. }).to.throw(/certificate must be an object/);
  173. expect(() => {
  174. dialog.showCertificateTrustDialog({ certificate: {} as any, message: false as any });
  175. }).to.throw(/message must be a string/);
  176. });
  177. });
  178. });