main.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Deprecated APIs are still supported and should be tested.
  2. process.throwDeprecation = false;
  3. const electron = require('electron');
  4. const { app, BrowserWindow, dialog, ipcMain, session } = electron;
  5. try {
  6. require('fs').rmdirSync(app.getPath('userData'), { recursive: true });
  7. } catch (e) {
  8. console.warn('Warning: couldn\'t clear user data directory:', e);
  9. }
  10. const fs = require('fs');
  11. const path = require('path');
  12. const util = require('util');
  13. const v8 = require('v8');
  14. const argv = require('yargs')
  15. .boolean('ci')
  16. .array('files')
  17. .string('g').alias('g', 'grep')
  18. .boolean('i').alias('i', 'invert')
  19. .argv;
  20. let window = null;
  21. v8.setFlagsFromString('--expose_gc');
  22. app.commandLine.appendSwitch('js-flags', '--expose_gc');
  23. app.commandLine.appendSwitch('ignore-certificate-errors');
  24. app.commandLine.appendSwitch('disable-renderer-backgrounding');
  25. // Some ports are considered to be "unsafe" by Chromium
  26. // but Windows on Microsoft-hosted agents sometimes assigns one of them
  27. // to a Node.js server. Chromium refuses to establish a connection
  28. // and the whole app crashes with the "Error: net::ERR_UNSAFE_PORT" error.
  29. // Let's allow connections to those ports to avoid test failures.
  30. // Use a comma-separated list of ports as a flag value, e.g. "666,667,668".
  31. app.commandLine.appendSwitch('explicitly-allowed-ports', '2049');
  32. // Disable security warnings (the security warnings test will enable them)
  33. process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
  34. // Accessing stdout in the main process will result in the process.stdout
  35. // throwing UnknownSystemError in renderer process sometimes. This line makes
  36. // sure we can reproduce it in renderer process.
  37. // eslint-disable-next-line
  38. process.stdout
  39. // Access console to reproduce #3482.
  40. // eslint-disable-next-line
  41. console
  42. ipcMain.on('message', function (event, ...args) {
  43. event.sender.send('message', ...args);
  44. });
  45. ipcMain.handle('get-modules', () => Object.keys(electron));
  46. ipcMain.handle('get-temp-dir', () => app.getPath('temp'));
  47. ipcMain.handle('ping', () => null);
  48. // Write output to file if OUTPUT_TO_FILE is defined.
  49. const outputToFile = process.env.OUTPUT_TO_FILE;
  50. const print = function (_, method, args) {
  51. const output = util.format.apply(null, args);
  52. if (outputToFile) {
  53. fs.appendFileSync(outputToFile, output + '\n');
  54. } else {
  55. console[method](output);
  56. }
  57. };
  58. ipcMain.on('console-call', print);
  59. ipcMain.on('process.exit', function (event, code) {
  60. process.exit(code);
  61. });
  62. ipcMain.on('eval', function (event, script) {
  63. event.returnValue = eval(script) // eslint-disable-line
  64. });
  65. ipcMain.on('echo', function (event, msg) {
  66. event.returnValue = msg;
  67. });
  68. process.removeAllListeners('uncaughtException');
  69. process.on('uncaughtException', function (error) {
  70. console.error(error, error.stack);
  71. process.exit(1);
  72. });
  73. global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS;
  74. app.on('window-all-closed', function () {
  75. app.quit();
  76. });
  77. app.on('child-process-gone', (event, details) => {
  78. if (details.type === 'GPU' && details.reason !== 'clean-exit') {
  79. if (details.reason === 'crashed') {
  80. console.log('GPU process crashed');
  81. } else {
  82. console.log(`GPU process exited with code ${details.exitCode}`);
  83. }
  84. }
  85. });
  86. app.on('renderer-process-crashed', (event, contents, killed) => {
  87. console.log(`webContents ${contents.id} crashed: ${contents.getURL()} (killed=${killed})`);
  88. });
  89. app.whenReady().then(async function () {
  90. await session.defaultSession.clearCache();
  91. await session.defaultSession.clearStorageData();
  92. // Test if using protocol module would crash.
  93. electron.protocol.registerStringProtocol('test-if-crashes', function () {});
  94. window = new BrowserWindow({
  95. title: 'Electron Tests',
  96. show: false,
  97. width: 800,
  98. height: 600,
  99. webPreferences: {
  100. backgroundThrottling: false,
  101. nodeIntegration: true,
  102. webviewTag: true,
  103. contextIsolation: false
  104. }
  105. });
  106. window.loadFile('static/index.html', {
  107. query: {
  108. grep: argv.grep,
  109. invert: argv.invert ? 'true' : '',
  110. files: argv.files ? argv.files.join(',') : undefined
  111. }
  112. });
  113. window.on('unresponsive', function () {
  114. const chosen = dialog.showMessageBox(window, {
  115. type: 'warning',
  116. buttons: ['Close', 'Keep Waiting'],
  117. message: 'Window is not responding',
  118. detail: 'The window is not responding. Would you like to force close it or just keep waiting?'
  119. });
  120. if (chosen === 0) window.destroy();
  121. });
  122. window.webContents.on('crashed', function () {
  123. console.error('Renderer process crashed');
  124. process.exit(1);
  125. });
  126. });
  127. ipcMain.on('prevent-next-will-attach-webview', (event) => {
  128. event.sender.once('will-attach-webview', event => event.preventDefault());
  129. });
  130. ipcMain.on('break-next-will-attach-webview', (event, id) => {
  131. event.sender.once('will-attach-webview', (event, webPreferences, params) => {
  132. params.instanceId = null;
  133. });
  134. });
  135. ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => {
  136. event.sender.once('will-attach-webview', (event, webPreferences, params) => {
  137. params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}`;
  138. webPreferences.nodeIntegration = false;
  139. });
  140. });
  141. ipcMain.on('disable-preload-on-next-will-attach-webview', (event, id) => {
  142. event.sender.once('will-attach-webview', (event, webPreferences, params) => {
  143. params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'webview-stripped-preload.html')}`;
  144. delete webPreferences.preload;
  145. });
  146. });
  147. ipcMain.on('handle-uncaught-exception', (event, message) => {
  148. suspendListeners(process, 'uncaughtException', (error) => {
  149. event.returnValue = error.message;
  150. });
  151. fs.readFile(__filename, () => {
  152. throw new Error(message);
  153. });
  154. });
  155. ipcMain.on('handle-unhandled-rejection', (event, message) => {
  156. suspendListeners(process, 'unhandledRejection', (error) => {
  157. event.returnValue = error.message;
  158. });
  159. fs.readFile(__filename, () => {
  160. Promise.reject(new Error(message));
  161. });
  162. });
  163. // Suspend listeners until the next event and then restore them
  164. const suspendListeners = (emitter, eventName, callback) => {
  165. const listeners = emitter.listeners(eventName);
  166. emitter.removeAllListeners(eventName);
  167. emitter.once(eventName, (...args) => {
  168. emitter.removeAllListeners(eventName);
  169. listeners.forEach((listener) => {
  170. emitter.on(eventName, listener);
  171. });
  172. // eslint-disable-next-line standard/no-callback-literal
  173. callback(...args);
  174. });
  175. };