api-browser-view-spec.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import { expect } from 'chai';
  2. import * as ChildProcess from 'child_process';
  3. import * as path from 'path';
  4. import { emittedOnce } from './events-helpers';
  5. import { BrowserView, BrowserWindow, webContents } from 'electron/main';
  6. import { closeWindow } from './window-helpers';
  7. describe('BrowserView module', () => {
  8. const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
  9. let w: BrowserWindow;
  10. let view: BrowserView;
  11. beforeEach(() => {
  12. w = new BrowserWindow({
  13. show: false,
  14. width: 400,
  15. height: 400,
  16. webPreferences: {
  17. backgroundThrottling: false
  18. }
  19. });
  20. });
  21. afterEach(async () => {
  22. await closeWindow(w);
  23. if (view) {
  24. view.destroy();
  25. }
  26. });
  27. describe('BrowserView.destroy()', () => {
  28. it('does not throw', () => {
  29. view = new BrowserView();
  30. view.destroy();
  31. });
  32. });
  33. describe('BrowserView.isDestroyed()', () => {
  34. it('returns correct value', () => {
  35. view = new BrowserView();
  36. expect(view.isDestroyed()).to.be.false('view is destroyed');
  37. view.destroy();
  38. expect(view.isDestroyed()).to.be.true('view is destroyed');
  39. });
  40. });
  41. it('can be created with an existing webContents', async () => {
  42. const wc = (webContents as any).create({ sandbox: true });
  43. await wc.loadURL('about:blank');
  44. view = new BrowserView({ webContents: wc } as any);
  45. expect(view.webContents.getURL()).to.equal('about:blank');
  46. });
  47. describe('BrowserView.setBackgroundColor()', () => {
  48. it('does not throw for valid args', () => {
  49. view = new BrowserView();
  50. view.setBackgroundColor('#000');
  51. });
  52. it('throws for invalid args', () => {
  53. view = new BrowserView();
  54. expect(() => {
  55. view.setBackgroundColor(null as any);
  56. }).to.throw(/conversion failure/);
  57. });
  58. });
  59. describe('BrowserView.setAutoResize()', () => {
  60. it('does not throw for valid args', () => {
  61. view = new BrowserView();
  62. view.setAutoResize({});
  63. view.setAutoResize({ width: true, height: false });
  64. });
  65. it('throws for invalid args', () => {
  66. view = new BrowserView();
  67. expect(() => {
  68. view.setAutoResize(null as any);
  69. }).to.throw(/conversion failure/);
  70. });
  71. });
  72. describe('BrowserView.setBounds()', () => {
  73. it('does not throw for valid args', () => {
  74. view = new BrowserView();
  75. view.setBounds({ x: 0, y: 0, width: 1, height: 1 });
  76. });
  77. it('throws for invalid args', () => {
  78. view = new BrowserView();
  79. expect(() => {
  80. view.setBounds(null as any);
  81. }).to.throw(/conversion failure/);
  82. expect(() => {
  83. view.setBounds({} as any);
  84. }).to.throw(/conversion failure/);
  85. });
  86. });
  87. describe('BrowserView.getBounds()', () => {
  88. it('returns the current bounds', () => {
  89. view = new BrowserView();
  90. const bounds = { x: 10, y: 20, width: 30, height: 40 };
  91. view.setBounds(bounds);
  92. expect(view.getBounds()).to.deep.equal(bounds);
  93. });
  94. });
  95. describe('BrowserWindow.setBrowserView()', () => {
  96. it('does not throw for valid args', () => {
  97. view = new BrowserView();
  98. w.setBrowserView(view);
  99. });
  100. it('does not throw if called multiple times with same view', () => {
  101. view = new BrowserView();
  102. w.setBrowserView(view);
  103. w.setBrowserView(view);
  104. w.setBrowserView(view);
  105. });
  106. });
  107. describe('BrowserWindow.getBrowserView()', () => {
  108. it('returns the set view', () => {
  109. view = new BrowserView();
  110. w.setBrowserView(view);
  111. expect(view.id).to.not.be.null('view id');
  112. const view2 = w.getBrowserView();
  113. expect(view2!.webContents.id).to.equal(view.webContents.id);
  114. });
  115. it('returns null if none is set', () => {
  116. const view = w.getBrowserView();
  117. expect(view).to.be.null('view');
  118. });
  119. });
  120. describe('BrowserWindow.addBrowserView()', () => {
  121. it('does not throw for valid args', () => {
  122. const view1 = new BrowserView();
  123. w.addBrowserView(view1);
  124. const view2 = new BrowserView();
  125. w.addBrowserView(view2);
  126. view1.destroy();
  127. view2.destroy();
  128. });
  129. it('does not throw if called multiple times with same view', () => {
  130. view = new BrowserView();
  131. w.addBrowserView(view);
  132. w.addBrowserView(view);
  133. w.addBrowserView(view);
  134. });
  135. it('can handle BrowserView reparenting', async () => {
  136. view = new BrowserView();
  137. w.addBrowserView(view);
  138. view.webContents.loadURL('about:blank');
  139. await emittedOnce(view.webContents, 'did-finish-load');
  140. const w2 = new BrowserWindow({ show: false });
  141. w2.addBrowserView(view);
  142. w.close();
  143. view.webContents.loadURL(`file://${fixtures}/pages/blank.html`);
  144. await emittedOnce(view.webContents, 'did-finish-load');
  145. // Clean up - the afterEach hook assumes the webContents on w is still alive.
  146. w = new BrowserWindow({ show: false });
  147. w2.close();
  148. w2.destroy();
  149. });
  150. });
  151. describe('BrowserWindow.removeBrowserView()', () => {
  152. it('does not throw if called multiple times with same view', () => {
  153. view = new BrowserView();
  154. w.addBrowserView(view);
  155. w.removeBrowserView(view);
  156. w.removeBrowserView(view);
  157. });
  158. });
  159. describe('BrowserWindow.getBrowserViews()', () => {
  160. it('returns same views as was added', () => {
  161. const view1 = new BrowserView();
  162. w.addBrowserView(view1);
  163. const view2 = new BrowserView();
  164. w.addBrowserView(view2);
  165. expect(view1.id).to.be.not.null('view id');
  166. const views = w.getBrowserViews();
  167. expect(views).to.have.lengthOf(2);
  168. expect(views[0].webContents.id).to.equal(view1.webContents.id);
  169. expect(views[1].webContents.id).to.equal(view2.webContents.id);
  170. view1.destroy();
  171. view2.destroy();
  172. });
  173. });
  174. describe('BrowserWindow.setTopBrowserView()', () => {
  175. it('should throw an error when a BrowserView is not attached to the window', () => {
  176. view = new BrowserView();
  177. expect(() => {
  178. w.setTopBrowserView(view);
  179. }).to.throw(/is not attached/);
  180. });
  181. it('should throw an error when a BrowserView is attached to some other window', () => {
  182. view = new BrowserView();
  183. const win2 = new BrowserWindow();
  184. w.addBrowserView(view);
  185. view.setBounds({ x: 0, y: 0, width: 100, height: 100 });
  186. win2.addBrowserView(view);
  187. expect(() => {
  188. w.setTopBrowserView(view);
  189. }).to.throw(/is not attached/);
  190. win2.close();
  191. win2.destroy();
  192. });
  193. });
  194. describe('BrowserView.webContents.getOwnerBrowserWindow()', () => {
  195. it('points to owning window', () => {
  196. view = new BrowserView();
  197. expect(view.webContents.getOwnerBrowserWindow()).to.be.null('owner browser window');
  198. w.setBrowserView(view);
  199. expect(view.webContents.getOwnerBrowserWindow()).to.equal(w);
  200. w.setBrowserView(null);
  201. expect(view.webContents.getOwnerBrowserWindow()).to.be.null('owner browser window');
  202. });
  203. });
  204. describe('BrowserView.fromId()', () => {
  205. it('returns the view with given id', () => {
  206. view = new BrowserView();
  207. w.setBrowserView(view);
  208. expect(view.id).to.not.be.null('view id');
  209. const view2 = BrowserView.fromId(view.id);
  210. expect(view2.webContents.id).to.equal(view.webContents.id);
  211. });
  212. });
  213. describe('BrowserView.fromWebContents()', () => {
  214. it('returns the view with given id', () => {
  215. view = new BrowserView();
  216. w.setBrowserView(view);
  217. expect(view.id).to.not.be.null('view id');
  218. const view2 = BrowserView.fromWebContents(view.webContents);
  219. expect(view2!.webContents.id).to.equal(view.webContents.id);
  220. });
  221. });
  222. describe('BrowserView.getAllViews()', () => {
  223. it('returns all views', () => {
  224. view = new BrowserView();
  225. w.setBrowserView(view);
  226. expect(view.id).to.not.be.null('view id');
  227. const views = BrowserView.getAllViews();
  228. expect(views).to.be.an('array').that.has.lengthOf(1);
  229. expect(views[0].webContents.id).to.equal(view.webContents.id);
  230. });
  231. });
  232. describe('new BrowserView()', () => {
  233. it('does not crash on exit', async () => {
  234. const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-browserview.js');
  235. const electronPath = process.execPath;
  236. const appProcess = ChildProcess.spawn(electronPath, [appPath]);
  237. const [code] = await emittedOnce(appProcess, 'exit');
  238. expect(code).to.equal(0);
  239. });
  240. });
  241. describe('window.open()', () => {
  242. it('works in BrowserView', async () => {
  243. view = new BrowserView();
  244. w.setBrowserView(view);
  245. const newWindow = emittedOnce(view.webContents, 'new-window');
  246. view.webContents.once('new-window', event => event.preventDefault());
  247. view.webContents.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
  248. const [, url, frameName,,, additionalFeatures] = await newWindow;
  249. expect(url).to.equal('http://host/');
  250. expect(frameName).to.equal('host');
  251. expect(additionalFeatures[0]).to.equal('this-is-not-a-standard-feature');
  252. });
  253. });
  254. });