api-dialog-spec.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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('does not crash when there is a defaultId but no buttons', 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. type: 'info',
  130. defaultId: 0,
  131. title: 'i am title'
  132. });
  133. controller.abort();
  134. const result = await p;
  135. expect(result.response).to.equal(0);
  136. });
  137. it('cancels message box', async () => {
  138. const controller = new AbortController();
  139. const signal = controller.signal;
  140. const w = new BrowserWindow();
  141. const p = dialog.showMessageBox(w, {
  142. signal,
  143. message: 'i am message',
  144. buttons: ['OK', 'Cancel'],
  145. cancelId: 1
  146. });
  147. controller.abort();
  148. const result = await p;
  149. expect(result.response).to.equal(1);
  150. });
  151. it('cancels message box after a while', async () => {
  152. const controller = new AbortController();
  153. const signal = controller.signal;
  154. const w = new BrowserWindow();
  155. const p = dialog.showMessageBox(w, {
  156. signal,
  157. message: 'i am message',
  158. buttons: ['OK', 'Cancel'],
  159. cancelId: 1
  160. });
  161. await setTimeout(500);
  162. controller.abort();
  163. const result = await p;
  164. expect(result.response).to.equal(1);
  165. });
  166. });
  167. describe('showErrorBox', () => {
  168. it('throws errors when the options are invalid', () => {
  169. expect(() => {
  170. (dialog.showErrorBox as any)();
  171. }).to.throw(/Insufficient number of arguments/);
  172. expect(() => {
  173. dialog.showErrorBox(3 as any, 'four');
  174. }).to.throw(/Error processing argument at index 0/);
  175. expect(() => {
  176. dialog.showErrorBox('three', 4 as any);
  177. }).to.throw(/Error processing argument at index 1/);
  178. });
  179. });
  180. describe('showCertificateTrustDialog', () => {
  181. it('throws errors when the options are invalid', () => {
  182. expect(() => {
  183. (dialog.showCertificateTrustDialog as any)();
  184. }).to.throw(/options must be an object/);
  185. expect(() => {
  186. dialog.showCertificateTrustDialog({} as any);
  187. }).to.throw(/certificate must be an object/);
  188. expect(() => {
  189. dialog.showCertificateTrustDialog({ certificate: {} as any, message: false as any });
  190. }).to.throw(/message must be a string/);
  191. });
  192. });
  193. });