visibility-state-spec.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { expect } from 'chai';
  2. import * as cp from 'child_process';
  3. import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain } from 'electron/main';
  4. import * as path from 'path';
  5. import { emittedOnce } from './events-helpers';
  6. import { closeWindow } from './window-helpers';
  7. import { ifdescribe, delay } from './spec-helpers';
  8. // visibilityState specs pass on linux with a real window manager but on CI
  9. // the environment does not let these specs pass
  10. ifdescribe(process.platform !== 'linux')('document.visibilityState', () => {
  11. let w: BrowserWindow;
  12. afterEach(() => {
  13. return closeWindow(w);
  14. });
  15. const load = () => w.loadFile(path.resolve(__dirname, 'fixtures', 'chromium', 'visibilitystate.html'));
  16. const itWithOptions = (name: string, options: BrowserWindowConstructorOptions, fn: Mocha.Func) => {
  17. return it(name, async function (...args) {
  18. w = new BrowserWindow({
  19. ...options,
  20. paintWhenInitiallyHidden: false,
  21. webPreferences: {
  22. ...(options.webPreferences || {}),
  23. nodeIntegration: true,
  24. contextIsolation: false
  25. }
  26. });
  27. await Promise.resolve(fn.apply(this, args));
  28. });
  29. };
  30. itWithOptions('should be visible when the window is initially shown by default', {}, async () => {
  31. load();
  32. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  33. expect(state).to.equal('visible');
  34. });
  35. itWithOptions('should be visible when the window is initially shown', {
  36. show: true
  37. }, async () => {
  38. load();
  39. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  40. expect(state).to.equal('visible');
  41. });
  42. itWithOptions('should be hidden when the window is initially hidden', {
  43. show: false
  44. }, async () => {
  45. load();
  46. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  47. expect(state).to.equal('hidden');
  48. });
  49. itWithOptions('should be visible when the window is initially hidden but shown before the page is loaded', {
  50. show: false
  51. }, async () => {
  52. w.show();
  53. load();
  54. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  55. expect(state).to.equal('visible');
  56. });
  57. itWithOptions('should be hidden when the window is initially shown but hidden before the page is loaded', {
  58. show: true
  59. }, async () => {
  60. // TODO(MarshallOfSound): Figure out if we can work around this 1 tick issue for users
  61. if (process.platform === 'darwin') {
  62. // Wait for a tick, the window being "shown" takes 1 tick on macOS
  63. await delay(10000);
  64. }
  65. w.hide();
  66. load();
  67. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  68. expect(state).to.equal('hidden');
  69. });
  70. itWithOptions('should be toggle between visible and hidden as the window is hidden and shown', {}, async () => {
  71. load();
  72. const [, initialState] = await emittedOnce(ipcMain, 'initial-visibility-state');
  73. expect(initialState).to.equal('visible');
  74. w.hide();
  75. await emittedOnce(ipcMain, 'visibility-change-hidden');
  76. w.show();
  77. await emittedOnce(ipcMain, 'visibility-change-visible');
  78. });
  79. itWithOptions('should become hidden when a window is minimized', {}, async () => {
  80. load();
  81. const [, initialState] = await emittedOnce(ipcMain, 'initial-visibility-state');
  82. expect(initialState).to.equal('visible');
  83. w.minimize();
  84. await emittedOnce(ipcMain, 'visibility-change-hidden', () => w.minimize());
  85. });
  86. itWithOptions('should become visible when a window is restored', {}, async () => {
  87. load();
  88. const [, initialState] = await emittedOnce(ipcMain, 'initial-visibility-state');
  89. expect(initialState).to.equal('visible');
  90. w.minimize();
  91. await emittedOnce(ipcMain, 'visibility-change-hidden');
  92. w.restore();
  93. await emittedOnce(ipcMain, 'visibility-change-visible');
  94. });
  95. describe('on platforms that support occlusion detection', () => {
  96. let child: cp.ChildProcess;
  97. before(function () {
  98. if (process.platform !== 'darwin') this.skip();
  99. });
  100. const makeOtherWindow = (opts: { x: number; y: number; width: number; height: number; }) => {
  101. child = cp.spawn(process.execPath, [path.resolve(__dirname, 'fixtures', 'chromium', 'other-window.js'), `${opts.x}`, `${opts.y}`, `${opts.width}`, `${opts.height}`]);
  102. return new Promise<void>(resolve => {
  103. child.stdout!.on('data', (chunk) => {
  104. if (chunk.toString().includes('__ready__')) resolve();
  105. });
  106. });
  107. };
  108. afterEach(() => {
  109. if (child && !child.killed) {
  110. child.kill('SIGTERM');
  111. }
  112. });
  113. itWithOptions('should be visible when two windows are on screen', {
  114. x: 0,
  115. y: 0,
  116. width: 200,
  117. height: 200
  118. }, async () => {
  119. await makeOtherWindow({
  120. x: 200,
  121. y: 0,
  122. width: 200,
  123. height: 200
  124. });
  125. load();
  126. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  127. expect(state).to.equal('visible');
  128. });
  129. itWithOptions('should be visible when two windows are on screen that overlap partially', {
  130. x: 50,
  131. y: 50,
  132. width: 150,
  133. height: 150
  134. }, async () => {
  135. await makeOtherWindow({
  136. x: 100,
  137. y: 0,
  138. width: 200,
  139. height: 200
  140. });
  141. load();
  142. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  143. expect(state).to.equal('visible');
  144. });
  145. itWithOptions('should be hidden when a second window completely occludes the current window', {
  146. x: 50,
  147. y: 50,
  148. width: 50,
  149. height: 50
  150. }, async function () {
  151. this.timeout(240000);
  152. load();
  153. const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state');
  154. expect(state).to.equal('visible');
  155. makeOtherWindow({
  156. x: 0,
  157. y: 0,
  158. width: 300,
  159. height: 300
  160. });
  161. await emittedOnce(ipcMain, 'visibility-change-hidden');
  162. });
  163. });
  164. });