Browse Source

test: move the last of the browser window specs (#19295)

* move devtools extensions tests

* move executeJavaScript tests

* move previewFile test

* move contextIsolation tests

* move OSR tests

* RIP api-browser-window-spec.js

          .--. .-,       .-..-.__
        .'(`.-` \_.-'-./`  |\_( "\__
     __.>\ ';  _;---,._|   / __/`'--)
    /.--.  : |/' _.--.<|  /  | |
_..-'    `\     /' /`  /_/ _/_/
 >_.-``-. `Y  /' _;---.`|/))))
'` .-''. \|:  .'   __, .-'"`
 .'--._ `-:  \/:  /'  '.\             _|_
     /.'`\ :;   /'      `-           `-|-`
    -`    |     |                      |
          :.; : |                  .-'~^~`-.
          |:    |                .' _     _ `.
          |:.   |                | |_) | |_) |
          :. :  |                | | \ | |   |
          : ;   |                |           |
          : ;   |                | Here lies |
          : ;   |                |   1000    |
          : ;   |                |   flaky   |
          : ;   |                |   tests   |
        .jgs. : ;                |           |
-."-/\\\/:::.    `\."-._'."-"_\\-|           |///."-
" -."-.\\"-."//.-".`-."_\\-.".-\\`=.........=`//-".

* remove unused ipcMain listeners

* remove debugging logs

* close windows in offscreen test

* more closeAllWindows

* remove extra logs

* refactor webContents main spec using closeAllWindows
Jeremy Apthorp 5 years ago
parent
commit
2a5d40617a
4 changed files with 600 additions and 755 deletions
  1. 473 10
      spec-main/api-browser-window-spec.ts
  2. 127 21
      spec-main/api-web-contents-spec.ts
  3. 0 704
      spec/api-browser-window-spec.js
  4. 0 20
      spec/static/main.js

+ 473 - 10
spec-main/api-browser-window-spec.ts

@@ -2448,15 +2448,12 @@ describe('BrowserWindow module', () => {
         throw new Error(`Unexpected visibility change event. visibilityState: ${visibilityState} hidden: ${hidden}`)
       })
       try {
-        console.log("c 1")
         const shown1 = emittedOnce(w, 'show')
         w.show()
         await shown1
-        console.log("c 2")
         const hidden = emittedOnce(w, 'hide')
         w.hide()
         await hidden
-        console.log("c 3")
         const shown2 = emittedOnce(w, 'show')
         w.show()
         await shown2
@@ -2682,31 +2679,24 @@ describe('BrowserWindow module', () => {
     ifdescribe(process.platform === 'win32')('on windows', () => {
       it('should restore a normal visible window from a fullscreen startup state', async () => {
         const w = new BrowserWindow({show: false})
-        console.log("a 1")
         await w.loadURL('about:blank')
-        console.log("a 2")
         const shown = emittedOnce(w, 'show')
         // start fullscreen and hidden
         w.setFullScreen(true)
         w.show()
         await shown
-        console.log("a 3")
         const leftFullScreen = emittedOnce(w, 'leave-full-screen')
         w.setFullScreen(false)
         await leftFullScreen
-        console.log("a 4")
         expect(w.isVisible()).to.be.true('visible')
         expect(w.isFullScreen()).to.be.false('fullscreen')
       })
       it('should keep window hidden if already in hidden state', async () => {
         const w = new BrowserWindow({show: false})
-        console.log("b 1")
         await w.loadURL('about:blank')
-        console.log("b 2")
         const leftFullScreen = emittedOnce(w, 'leave-full-screen')
         w.setFullScreen(false)
         await leftFullScreen
-        console.log("b 3")
         expect(w.isVisible()).to.be.false('visible')
         expect(w.isFullScreen()).to.be.false('fullscreen')
       })
@@ -3328,4 +3318,477 @@ describe('BrowserWindow module', () => {
     })
   })
 
+  ifdescribe(!process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS)('window.getNativeWindowHandle()', () => {
+    afterEach(closeAllWindows)
+    it('returns valid handle', () => {
+      const w = new BrowserWindow({show: false})
+      // The module's source code is hosted at
+      // https://github.com/electron/node-is-valid-window
+      const isValidWindow = require('is-valid-window')
+      expect(isValidWindow(w.getNativeWindowHandle())).to.be.true('is valid window')
+    })
+  })
+
+  describe('extensions and dev tools extensions', () => {
+    let showPanelTimeoutId: NodeJS.Timeout | null = null
+
+    const showLastDevToolsPanel = (w: BrowserWindow) => {
+      w.webContents.once('devtools-opened', () => {
+        const show = () => {
+          if (w == null || w.isDestroyed()) return
+          const { devToolsWebContents } = w as unknown as { devToolsWebContents: WebContents | undefined }
+          if (devToolsWebContents == null || devToolsWebContents.isDestroyed()) {
+            return
+          }
+
+          const showLastPanel = () => {
+            // this is executed in the devtools context, where UI is a global
+            const {UI} = (window as any)
+            const lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id
+            UI.inspectorView.showPanel(lastPanelId)
+          }
+          devToolsWebContents.executeJavaScript(`(${showLastPanel})()`, false).then(() => {
+            showPanelTimeoutId = setTimeout(show, 100)
+          })
+        }
+        showPanelTimeoutId = setTimeout(show, 100)
+      })
+    }
+
+    afterEach(() => {
+      if (showPanelTimeoutId != null) {
+        clearTimeout(showPanelTimeoutId)
+        showPanelTimeoutId = null
+      }
+    })
+
+    describe('BrowserWindow.addDevToolsExtension', () => {
+      describe('for invalid extensions', () => {
+        it('throws errors for missing manifest.json files', () => {
+          const nonexistentExtensionPath = path.join(__dirname, 'does-not-exist')
+          expect(() => {
+            BrowserWindow.addDevToolsExtension(nonexistentExtensionPath)
+          }).to.throw(/ENOENT: no such file or directory/)
+        })
+
+        it('throws errors for invalid manifest.json files', () => {
+          const badManifestExtensionPath = path.join(fixtures, 'devtools-extensions', 'bad-manifest')
+          expect(() => {
+            BrowserWindow.addDevToolsExtension(badManifestExtensionPath)
+          }).to.throw(/Unexpected token }/)
+        })
+      })
+
+      describe('for a valid extension', () => {
+        const extensionName = 'foo'
+
+        before(() => {
+          const extensionPath = path.join(fixtures, 'devtools-extensions', 'foo')
+          BrowserWindow.addDevToolsExtension(extensionPath)
+          expect(BrowserWindow.getDevToolsExtensions()).to.have.property(extensionName)
+        })
+
+        after(() => {
+          BrowserWindow.removeDevToolsExtension('foo')
+          expect(BrowserWindow.getDevToolsExtensions()).to.not.have.property(extensionName)
+        })
+
+        describe('when the devtools is docked', () => {
+          let message: any
+          let w: BrowserWindow
+          before(async () => {
+            w = new BrowserWindow({show: false, webPreferences: {nodeIntegration: true}})
+            const p = new Promise(resolve => ipcMain.once('answer', (event, message) => {
+              resolve(message)
+            }))
+            showLastDevToolsPanel(w)
+            w.loadURL('about:blank')
+            w.webContents.openDevTools({ mode: 'bottom' })
+            message = await p
+          })
+          after(closeAllWindows)
+
+          describe('created extension info', function () {
+            it('has proper "runtimeId"', async function () {
+              expect(message).to.have.ownProperty('runtimeId')
+              expect(message.runtimeId).to.equal(extensionName)
+            })
+            it('has "tabId" matching webContents id', function () {
+              expect(message).to.have.ownProperty('tabId')
+              expect(message.tabId).to.equal(w.webContents.id)
+            })
+            it('has "i18nString" with proper contents', function () {
+              expect(message).to.have.ownProperty('i18nString')
+              expect(message.i18nString).to.equal('foo - bar (baz)')
+            })
+            it('has "storageItems" with proper contents', function () {
+              expect(message).to.have.ownProperty('storageItems')
+              expect(message.storageItems).to.deep.equal({
+                local: {
+                  set: { hello: 'world', world: 'hello' },
+                  remove: { world: 'hello' },
+                  clear: {}
+                },
+                sync: {
+                  set: { foo: 'bar', bar: 'foo' },
+                  remove: { foo: 'bar' },
+                  clear: {}
+                }
+              })
+            })
+          })
+        })
+
+        describe('when the devtools is undocked', () => {
+          let message: any
+          let w: BrowserWindow
+          before(async () => {
+            w = new BrowserWindow({show: false, webPreferences: {nodeIntegration: true}})
+            showLastDevToolsPanel(w)
+            w.loadURL('about:blank')
+            w.webContents.openDevTools({ mode: 'undocked' })
+            message = await new Promise(resolve => ipcMain.once('answer', (event, message) => {
+              resolve(message)
+            }))
+          })
+          after(closeAllWindows)
+
+          describe('created extension info', function () {
+            it('has proper "runtimeId"', function () {
+              expect(message).to.have.ownProperty('runtimeId')
+              expect(message.runtimeId).to.equal(extensionName)
+            })
+            it('has "tabId" matching webContents id', function () {
+              expect(message).to.have.ownProperty('tabId')
+              expect(message.tabId).to.equal(w.webContents.id)
+            })
+          })
+        })
+      })
+    })
+
+    it('works when used with partitions', async () => {
+      const w = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          nodeIntegration: true,
+          partition: 'temp'
+        }
+      })
+
+      const extensionPath = path.join(fixtures, 'devtools-extensions', 'foo')
+      BrowserWindow.addDevToolsExtension(extensionPath)
+      try {
+        showLastDevToolsPanel(w)
+
+        const p: Promise<any> = new Promise(resolve => ipcMain.once('answer', function (event, message) {
+          resolve(message)
+        }))
+
+        w.loadURL('about:blank')
+        w.webContents.openDevTools({ mode: 'bottom' })
+        const message = await p
+        expect(message.runtimeId).to.equal('foo')
+      } finally {
+        BrowserWindow.removeDevToolsExtension('foo')
+        await closeAllWindows()
+      }
+    })
+
+    it('serializes the registered extensions on quit', () => {
+      const extensionName = 'foo'
+      const extensionPath = path.join(fixtures, 'devtools-extensions', extensionName)
+      const serializedPath = path.join(app.getPath('userData'), 'DevTools Extensions')
+
+      BrowserWindow.addDevToolsExtension(extensionPath)
+      app.emit('will-quit')
+      expect(JSON.parse(fs.readFileSync(serializedPath, 'utf8'))).to.deep.equal([extensionPath])
+
+      BrowserWindow.removeDevToolsExtension(extensionName)
+      app.emit('will-quit')
+      expect(fs.existsSync(serializedPath)).to.be.false('file exists')
+    })
+
+    describe('BrowserWindow.addExtension', () => {
+      it('throws errors for missing manifest.json files', () => {
+        expect(() => {
+          BrowserWindow.addExtension(path.join(__dirname, 'does-not-exist'))
+        }).to.throw('ENOENT: no such file or directory')
+      })
+
+      it('throws errors for invalid manifest.json files', () => {
+        expect(() => {
+          BrowserWindow.addExtension(path.join(fixtures, 'devtools-extensions', 'bad-manifest'))
+        }).to.throw('Unexpected token }')
+      })
+    })
+  })
+
+  ifdescribe(process.platform === 'darwin')('previewFile', () => {
+    afterEach(closeAllWindows)
+    it('opens the path in Quick Look on macOS', () => {
+      const w = new BrowserWindow({show: false})
+      expect(() => {
+        w.previewFile(__filename)
+        w.closeFilePreview()
+      }).to.not.throw()
+    })
+  })
+
+  describe('contextIsolation option with and without sandbox option', () => {
+    const expectedContextData = {
+      preloadContext: {
+        preloadProperty: 'number',
+        pageProperty: 'undefined',
+        typeofRequire: 'function',
+        typeofProcess: 'object',
+        typeofArrayPush: 'function',
+        typeofFunctionApply: 'function',
+        typeofPreloadExecuteJavaScriptProperty: 'undefined'
+      },
+      pageContext: {
+        preloadProperty: 'undefined',
+        pageProperty: 'string',
+        typeofRequire: 'undefined',
+        typeofProcess: 'undefined',
+        typeofArrayPush: 'number',
+        typeofFunctionApply: 'boolean',
+        typeofPreloadExecuteJavaScriptProperty: 'number',
+        typeofOpenedWindow: 'object'
+      }
+    }
+
+    afterEach(closeAllWindows)
+
+    it('separates the page context from the Electron/preload context', async () => {
+      const iw = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-preload.js')
+        }
+      })
+      const p = emittedOnce(ipcMain, 'isolated-world')
+      iw.loadFile(path.join(fixtures, 'api', 'isolated.html'))
+      const [, data] = await p
+      expect(data).to.deep.equal(expectedContextData)
+    })
+    it('recreates the contexts on reload', async () => {
+      const iw = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-preload.js')
+        }
+      })
+      await iw.loadFile(path.join(fixtures, 'api', 'isolated.html'))
+      const isolatedWorld = emittedOnce(ipcMain, 'isolated-world')
+      iw.webContents.reload()
+      const [, data] = await isolatedWorld
+      expect(data).to.deep.equal(expectedContextData)
+    })
+    it('enables context isolation on child windows', async () => {
+      const iw = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-preload.js')
+        }
+      })
+      const browserWindowCreated = emittedOnce(app, 'browser-window-created')
+      iw.loadFile(path.join(fixtures, 'pages', 'window-open.html'))
+      const [, window] = await browserWindowCreated
+      expect(window.webContents.getLastWebPreferences().contextIsolation).to.be.true('contextIsolation')
+    })
+    it('separates the page context from the Electron/preload context with sandbox on', async () => {
+      const ws = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          sandbox: true,
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-preload.js')
+        }
+      })
+      const p = emittedOnce(ipcMain, 'isolated-world')
+      ws.loadFile(path.join(fixtures, 'api', 'isolated.html'))
+      const [, data] = await p
+      expect(data).to.deep.equal(expectedContextData)
+    })
+    it('recreates the contexts on reload with sandbox on', async () => {
+      const ws = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          sandbox: true,
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-preload.js')
+        }
+      })
+      await ws.loadFile(path.join(fixtures, 'api', 'isolated.html'))
+      const isolatedWorld = emittedOnce(ipcMain, 'isolated-world')
+      ws.webContents.reload()
+      const [, data] = await isolatedWorld
+      expect(data).to.deep.equal(expectedContextData)
+    })
+    it('supports fetch api', async () => {
+      const fetchWindow = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-fetch-preload.js')
+        }
+      })
+      const p = emittedOnce(ipcMain, 'isolated-fetch-error')
+      fetchWindow.loadURL('about:blank')
+      const [, error] = await p
+      expect(error).to.equal('Failed to fetch')
+    })
+    it('doesn\'t break ipc serialization', async () => {
+      const iw = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          contextIsolation: true,
+          preload: path.join(fixtures, 'api', 'isolated-preload.js')
+        }
+      })
+      const p = emittedOnce(ipcMain, 'isolated-world')
+      iw.loadURL('about:blank')
+      iw.webContents.executeJavaScript(`
+        const opened = window.open()
+        openedLocation = opened.location.href
+        opened.close()
+        window.postMessage({openedLocation}, '*')
+      `)
+      const [, data] = await p
+      expect(data.pageContext.openedLocation).to.equal('')
+    })
+  })
+
+  const features = process.electronBinding('features')
+  ifdescribe(features.isOffscreenRenderingEnabled())('offscreen rendering', () => {
+    let w: BrowserWindow
+    beforeEach(function () {
+      w = new BrowserWindow({
+        width: 100,
+        height: 100,
+        show: false,
+        webPreferences: {
+          backgroundThrottling: false,
+          offscreen: true
+        }
+      })
+    })
+    afterEach(closeAllWindows)
+
+    it('creates offscreen window with correct size', (done) => {
+      w.webContents.once('paint', function (event, rect, data) {
+        expect(data.constructor.name).to.equal('NativeImage')
+        expect(data.isEmpty()).to.be.false('data is empty')
+        const size = data.getSize()
+        const { scaleFactor } = screen.getPrimaryDisplay()
+        expect(size.width).to.be.closeTo(100 * scaleFactor, 2)
+        expect(size.height).to.be.closeTo(100 * scaleFactor, 2)
+        done()
+      })
+      w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+    })
+
+    it('does not crash after navigation', () => {
+      w.webContents.loadURL('about:blank')
+      w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+    })
+
+    describe('window.webContents.isOffscreen()', () => {
+      it('is true for offscreen type', () => {
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+        expect(w.webContents.isOffscreen()).to.be.true('isOffscreen')
+      })
+
+      it('is false for regular window', () => {
+        const c = new BrowserWindow({ show: false })
+        expect(c.webContents.isOffscreen()).to.be.false('isOffscreen')
+        c.destroy()
+      })
+    })
+
+    describe('window.webContents.isPainting()', () => {
+      it('returns whether is currently painting', (done) => {
+        w.webContents.once('paint', function (event, rect, data) {
+          expect(w.webContents.isPainting()).to.be.true('isPainting')
+          done()
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+    })
+
+    describe('window.webContents.stopPainting()', () => {
+      it('stops painting', (done) => {
+        w.webContents.on('dom-ready', () => {
+          w.webContents.stopPainting()
+          expect(w.webContents.isPainting()).to.be.false('isPainting')
+          done()
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+    })
+
+    describe('window.webContents.startPainting()', () => {
+      it('starts painting', (done) => {
+        w.webContents.on('dom-ready', () => {
+          w.webContents.stopPainting()
+          w.webContents.startPainting()
+          w.webContents.once('paint', function (event, rect, data) {
+            expect(w.webContents.isPainting()).to.be.true('isPainting')
+            done()
+          })
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+    })
+
+    // TODO(codebytere): remove in Electron v8.0.0
+    describe('window.webContents.getFrameRate()', () => {
+      it('has default frame rate', (done) => {
+        w.webContents.once('paint', function (event, rect, data) {
+          expect(w.webContents.getFrameRate()).to.equal(60)
+          done()
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+    })
+
+    // TODO(codebytere): remove in Electron v8.0.0
+    describe('window.webContents.setFrameRate(frameRate)', () => {
+      it('sets custom frame rate', (done) => {
+        w.webContents.on('dom-ready', () => {
+          w.webContents.setFrameRate(30)
+          w.webContents.once('paint', function (event, rect, data) {
+            expect(w.webContents.getFrameRate()).to.equal(30)
+            done()
+          })
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+    })
+
+    describe('window.webContents.FrameRate', () => {
+      it('has default frame rate', (done) => {
+        w.webContents.once('paint', function (event, rect, data) {
+          expect(w.webContents.frameRate).to.equal(60)
+          done()
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+
+      it('sets custom frame rate', (done) => {
+        w.webContents.on('dom-ready', () => {
+          w.webContents.frameRate = 30
+          w.webContents.once('paint', function (event, rect, data) {
+            expect(w.webContents.frameRate).to.equal(30)
+            done()
+          })
+        })
+        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
+      })
+    })
+  })
 })

+ 127 - 21
spec-main/api-web-contents-spec.ts

@@ -1,9 +1,11 @@
 import * as chai from 'chai'
+import { AddressInfo } from 'net'
 import * as chaiAsPromised from 'chai-as-promised'
 import * as path from 'path'
+import * as http from 'http'
 import { BrowserWindow, webContents } from 'electron'
 import { emittedOnce } from './events-helpers';
-import { closeWindow } from './window-helpers';
+import { closeAllWindows } from './window-helpers';
 
 const { expect } = chai
 
@@ -12,28 +14,13 @@ chai.use(chaiAsPromised)
 const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures')
 
 describe('webContents module', () => {
-  let w: BrowserWindow = (null as unknown as BrowserWindow)
-
-  beforeEach(() => {
-    w = new BrowserWindow({
-      show: false,
-      width: 400,
-      height: 400,
-      webPreferences: {
-        backgroundThrottling: false,
-        nodeIntegration: true,
-        webviewTag: true
-      }
-    })
-  })
-
-  afterEach(async () => {
-    await closeWindow(w)
-    w = (null as unknown as BrowserWindow)
-  })
-
   describe('getAllWebContents() API', () => {
+    afterEach(closeAllWindows)
     it('returns an array of web contents', async () => {
+      const w = new BrowserWindow({
+        show: false,
+        webPreferences: { webviewTag: true }
+      })
       w.loadFile(path.join(fixturesPath, 'pages', 'webview-zoom-factor.html'))
 
       await emittedOnce(w.webContents, 'did-attach-webview')
@@ -54,7 +41,9 @@ describe('webContents module', () => {
   })
 
   describe('will-prevent-unload event', () => {
+    afterEach(closeAllWindows)
     it('does not emit if beforeunload returns undefined', (done) => {
+      const w = new BrowserWindow({show: false})
       w.once('closed', () => done())
       w.webContents.once('will-prevent-unload', (e) => {
         expect.fail('should not have fired')
@@ -63,14 +52,131 @@ describe('webContents module', () => {
     })
 
     it('emits if beforeunload returns false', (done) => {
+      const w = new BrowserWindow({show: false})
       w.webContents.once('will-prevent-unload', () => done())
       w.loadFile(path.join(fixturesPath, 'api', 'close-beforeunload-false.html'))
     })
 
     it('supports calling preventDefault on will-prevent-unload events', (done) => {
+      const w = new BrowserWindow({show: false})
       w.webContents.once('will-prevent-unload', event => event.preventDefault())
       w.once('closed', () => done())
       w.loadFile(path.join(fixturesPath, 'api', 'close-beforeunload-false.html'))
     })
   })
+
+  describe('webContents.send(channel, args...)', () => {
+    afterEach(closeAllWindows)
+    it('throws an error when the channel is missing', () => {
+      const w = new BrowserWindow({show: false})
+      expect(() => {
+        (w.webContents.send as any)()
+      }).to.throw('Missing required channel argument')
+
+      expect(() => {
+        w.webContents.send(null as any)
+      }).to.throw('Missing required channel argument')
+    })
+  })
+
+  describe('webContents.executeJavaScript', () => {
+    describe('in about:blank', () => {
+      const expected = 'hello, world!'
+      const expectedErrorMsg = 'woops!'
+      const code = `(() => "${expected}")()`
+      const asyncCode = `(() => new Promise(r => setTimeout(() => r("${expected}"), 500)))()`
+      const badAsyncCode = `(() => new Promise((r, e) => setTimeout(() => e("${expectedErrorMsg}"), 500)))()`
+      const errorTypes = new Set([
+        Error,
+        ReferenceError,
+        EvalError,
+        RangeError,
+        SyntaxError,
+        TypeError,
+        URIError
+      ])
+      let w: BrowserWindow
+
+      before(async () => {
+        w = new BrowserWindow({show: false})
+        await w.loadURL('about:blank')
+      })
+      after(closeAllWindows)
+
+      it('resolves the returned promise with the result', async () => {
+        const result = await w.webContents.executeJavaScript(code)
+        expect(result).to.equal(expected)
+      })
+      it('resolves the returned promise with the result if the code returns an asyncronous promise', async () => {
+        const result = await w.webContents.executeJavaScript(asyncCode)
+        expect(result).to.equal(expected)
+      })
+      it('rejects the returned promise if an async error is thrown', async () => {
+        await expect(w.webContents.executeJavaScript(badAsyncCode)).to.eventually.be.rejectedWith(expectedErrorMsg)
+      })
+      it('rejects the returned promise with an error if an Error.prototype is thrown', async () => {
+        for (const error of errorTypes) {
+          await expect(w.webContents.executeJavaScript(`Promise.reject(new ${error.name}("Wamp-wamp"))`))
+            .to.eventually.be.rejectedWith(error)
+        }
+      })
+    })
+
+    describe("on a real page", () => {
+      let w: BrowserWindow
+      beforeEach(() => {
+        w = new BrowserWindow({show: false})
+      })
+      afterEach(closeAllWindows)
+
+      let server: http.Server = null as unknown as http.Server
+      let serverUrl: string = null as unknown as string
+
+      before((done) => {
+        server = http.createServer((request, response) => {
+          response.end()
+        }).listen(0, '127.0.0.1', () => {
+          serverUrl = 'http://127.0.0.1:' + (server.address() as AddressInfo).port
+          done()
+        })
+      })
+
+      after(() => {
+        server.close()
+      })
+
+      it('works after page load and during subframe load', (done) => {
+        w.webContents.once('did-finish-load', () => {
+          // initiate a sub-frame load, then try and execute script during it
+          w.webContents.executeJavaScript(`
+            var iframe = document.createElement('iframe')
+            iframe.src = '${serverUrl}/slow'
+            document.body.appendChild(iframe)
+          `).then(() => {
+            w.webContents.executeJavaScript('console.log(\'hello\')').then(() => {
+              done()
+            })
+          })
+        })
+        w.loadURL(serverUrl)
+      })
+
+      it('executes after page load', (done) => {
+        w.webContents.executeJavaScript(`(() => "test")()`).then(result => {
+          expect(result).to.equal("test")
+          done()
+        })
+        w.loadURL(serverUrl)
+      })
+
+      it('works with result objects that have DOM class prototypes', (done) => {
+        w.webContents.executeJavaScript('document.location').then(result => {
+          expect(result.origin).to.equal(serverUrl)
+          expect(result.protocol).to.equal('http:')
+          done()
+        })
+        w.loadURL(serverUrl)
+      })
+    })
+  })
 })

+ 0 - 704
spec/api-browser-window-spec.js

@@ -1,704 +0,0 @@
-'use strict'
-
-const chai = require('chai')
-const dirtyChai = require('dirty-chai')
-const fs = require('fs')
-const path = require('path')
-const os = require('os')
-const qs = require('querystring')
-const http = require('http')
-const { closeWindow } = require('./window-helpers')
-const { emittedOnce } = require('./events-helpers')
-const { ipcRenderer, remote } = require('electron')
-const { app, ipcMain, BrowserWindow, BrowserView, protocol, session, screen, webContents } = remote
-
-const features = process.electronBinding('features')
-const { expect } = chai
-const isCI = remote.getGlobal('isCi')
-const nativeModulesEnabled = remote.getGlobal('nativeModulesEnabled')
-
-chai.use(dirtyChai)
-
-describe('BrowserWindow module', () => {
-  const fixtures = path.resolve(__dirname, 'fixtures')
-  let w = null
-  let iw = null
-  let ws = null
-  let server
-  let postData
-
-  const defaultOptions = {
-    show: false,
-    width: 400,
-    height: 400,
-    webPreferences: {
-      backgroundThrottling: false,
-      nodeIntegration: true
-    }
-  }
-
-  const openTheWindow = async (options = defaultOptions) => {
-    // The `afterEach` hook isn't called if a test fails,
-    // we should make sure that the window is closed ourselves.
-    await closeTheWindow()
-
-    w = new BrowserWindow(options)
-    return w
-  }
-
-  const closeTheWindow = function () {
-    return closeWindow(w).then(() => { w = null })
-  }
-
-  before((done) => {
-    const filePath = path.join(fixtures, 'pages', 'a.html')
-    const fileStats = fs.statSync(filePath)
-    postData = [
-      {
-        type: 'rawData',
-        bytes: Buffer.from('username=test&file=')
-      },
-      {
-        type: 'file',
-        filePath: filePath,
-        offset: 0,
-        length: fileStats.size,
-        modificationTime: fileStats.mtime.getTime() / 1000
-      }
-    ]
-    server = http.createServer((req, res) => {
-      function respond () {
-        if (req.method === 'POST') {
-          let body = ''
-          req.on('data', (data) => {
-            if (data) body += data
-          })
-          req.on('end', () => {
-            const parsedData = qs.parse(body)
-            fs.readFile(filePath, (err, data) => {
-              if (err) return
-              if (parsedData.username === 'test' &&
-                  parsedData.file === data.toString()) {
-                res.end()
-              }
-            })
-          })
-        } else if (req.url === '/302') {
-          res.setHeader('Location', '/200')
-          res.statusCode = 302
-          res.end()
-        } else if (req.url === '/navigate-302') {
-          res.end(`<html><body><script>window.location='${server.url}/302'</script></body></html>`)
-        } else if (req.url === '/cross-site') {
-          res.end(`<html><body><h1>${req.url}</h1></body></html>`)
-        } else {
-          res.end()
-        }
-      }
-      setTimeout(respond, req.url.includes('slow') ? 200 : 0)
-    })
-    server.listen(0, '127.0.0.1', () => {
-      server.url = `http://127.0.0.1:${server.address().port}`
-      done()
-    })
-  })
-
-  after(() => {
-    server.close()
-    server = null
-  })
-
-  beforeEach(openTheWindow)
-
-  afterEach(closeTheWindow)
-
-  describe('window.webContents.send(channel, args...)', () => {
-    it('throws an error when the channel is missing', () => {
-      expect(() => {
-        w.webContents.send()
-      }).to.throw('Missing required channel argument')
-
-      expect(() => {
-        w.webContents.send(null)
-      }).to.throw('Missing required channel argument')
-    })
-  })
-
-  describe('window.getNativeWindowHandle()', () => {
-    before(function () {
-      if (!nativeModulesEnabled) {
-        this.skip()
-      }
-    })
-
-    it('returns valid handle', () => {
-      // The module's source code is hosted at
-      // https://github.com/electron/node-is-valid-window
-      const isValidWindow = remote.require('is-valid-window')
-      expect(isValidWindow(w.getNativeWindowHandle())).to.be.true()
-    })
-  })
-
-  describe('extensions and dev tools extensions', () => {
-    let showPanelTimeoutId
-
-    const showLastDevToolsPanel = () => {
-      w.webContents.once('devtools-opened', () => {
-        const show = () => {
-          if (w == null || w.isDestroyed()) return
-          const { devToolsWebContents } = w
-          if (devToolsWebContents == null || devToolsWebContents.isDestroyed()) {
-            return
-          }
-
-          const showLastPanel = () => {
-            const lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id
-            UI.inspectorView.showPanel(lastPanelId)
-          }
-          devToolsWebContents.executeJavaScript(`(${showLastPanel})()`, false).then(() => {
-            showPanelTimeoutId = setTimeout(show, 100)
-          })
-        }
-        showPanelTimeoutId = setTimeout(show, 100)
-      })
-    }
-
-    afterEach(() => {
-      clearTimeout(showPanelTimeoutId)
-    })
-
-    describe('BrowserWindow.addDevToolsExtension', () => {
-      describe('for invalid extensions', () => {
-        it('throws errors for missing manifest.json files', () => {
-          const nonexistentExtensionPath = path.join(__dirname, 'does-not-exist')
-          expect(() => {
-            BrowserWindow.addDevToolsExtension(nonexistentExtensionPath)
-          }).to.throw(/ENOENT: no such file or directory/)
-        })
-
-        it('throws errors for invalid manifest.json files', () => {
-          const badManifestExtensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest')
-          expect(() => {
-            BrowserWindow.addDevToolsExtension(badManifestExtensionPath)
-          }).to.throw(/Unexpected token }/)
-        })
-      })
-
-      describe('for a valid extension', () => {
-        const extensionName = 'foo'
-
-        const removeExtension = () => {
-          BrowserWindow.removeDevToolsExtension('foo')
-          expect(BrowserWindow.getDevToolsExtensions()).to.not.have.a.property(extensionName)
-        }
-
-        const addExtension = () => {
-          const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
-          BrowserWindow.addDevToolsExtension(extensionPath)
-          expect(BrowserWindow.getDevToolsExtensions()).to.have.a.property(extensionName)
-
-          showLastDevToolsPanel()
-
-          w.loadURL('about:blank')
-        }
-
-        // After* hooks won't be called if a test fail.
-        // So let's make a clean-up in the before hook.
-        beforeEach(removeExtension)
-
-        describe('when the devtools is docked', () => {
-          beforeEach(function (done) {
-            addExtension()
-            w.webContents.openDevTools({ mode: 'bottom' })
-            ipcMain.once('answer', (event, message) => {
-              this.message = message
-              done()
-            })
-          })
-
-          describe('created extension info', function () {
-            it('has proper "runtimeId"', function () {
-              expect(this.message).to.have.own.property('runtimeId')
-              expect(this.message.runtimeId).to.equal(extensionName)
-            })
-            it('has "tabId" matching webContents id', function () {
-              expect(this.message).to.have.own.property('tabId')
-              expect(this.message.tabId).to.equal(w.webContents.id)
-            })
-            it('has "i18nString" with proper contents', function () {
-              expect(this.message).to.have.own.property('i18nString')
-              expect(this.message.i18nString).to.equal('foo - bar (baz)')
-            })
-            it('has "storageItems" with proper contents', function () {
-              expect(this.message).to.have.own.property('storageItems')
-              expect(this.message.storageItems).to.deep.equal({
-                local: {
-                  set: { hello: 'world', world: 'hello' },
-                  remove: { world: 'hello' },
-                  clear: {}
-                },
-                sync: {
-                  set: { foo: 'bar', bar: 'foo' },
-                  remove: { foo: 'bar' },
-                  clear: {}
-                }
-              })
-            })
-          })
-        })
-
-        describe('when the devtools is undocked', () => {
-          beforeEach(function (done) {
-            addExtension()
-            w.webContents.openDevTools({ mode: 'undocked' })
-            ipcMain.once('answer', (event, message, extensionId) => {
-              this.message = message
-              done()
-            })
-          })
-
-          describe('created extension info', function () {
-            it('has proper "runtimeId"', function () {
-              expect(this.message).to.have.own.property('runtimeId')
-              expect(this.message.runtimeId).to.equal(extensionName)
-            })
-            it('has "tabId" matching webContents id', function () {
-              expect(this.message).to.have.own.property('tabId')
-              expect(this.message.tabId).to.equal(w.webContents.id)
-            })
-          })
-        })
-      })
-    })
-
-    it('works when used with partitions', (done) => {
-      if (w != null) {
-        w.destroy()
-      }
-      w = new BrowserWindow({
-        show: false,
-        webPreferences: {
-          nodeIntegration: true,
-          partition: 'temp'
-        }
-      })
-
-      const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
-      BrowserWindow.removeDevToolsExtension('foo')
-      BrowserWindow.addDevToolsExtension(extensionPath)
-
-      showLastDevToolsPanel()
-
-      ipcMain.once('answer', function (event, message) {
-        expect(message.runtimeId).to.equal('foo')
-        done()
-      })
-
-      w.loadURL('about:blank')
-      w.webContents.openDevTools({ mode: 'bottom' })
-    })
-
-    it('serializes the registered extensions on quit', () => {
-      const extensionName = 'foo'
-      const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName)
-      const serializedPath = path.join(app.getPath('userData'), 'DevTools Extensions')
-
-      BrowserWindow.addDevToolsExtension(extensionPath)
-      app.emit('will-quit')
-      expect(JSON.parse(fs.readFileSync(serializedPath))).to.deep.equal([extensionPath])
-
-      BrowserWindow.removeDevToolsExtension(extensionName)
-      app.emit('will-quit')
-      expect(fs.existsSync(serializedPath)).to.be.false()
-    })
-
-    describe('BrowserWindow.addExtension', () => {
-      beforeEach(() => {
-        BrowserWindow.removeExtension('foo')
-        expect(BrowserWindow.getExtensions()).to.not.have.property('foo')
-
-        const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
-        BrowserWindow.addExtension(extensionPath)
-        expect(BrowserWindow.getExtensions()).to.have.property('foo')
-
-        showLastDevToolsPanel()
-
-        w.loadURL('about:blank')
-      })
-
-      it('throws errors for missing manifest.json files', () => {
-        expect(() => {
-          BrowserWindow.addExtension(path.join(__dirname, 'does-not-exist'))
-        }).to.throw('ENOENT: no such file or directory')
-      })
-
-      it('throws errors for invalid manifest.json files', () => {
-        expect(() => {
-          BrowserWindow.addExtension(path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest'))
-        }).to.throw('Unexpected token }')
-      })
-    })
-  })
-
-  describe('window.webContents.executeJavaScript', () => {
-    const expected = 'hello, world!'
-    const expectedErrorMsg = 'woops!'
-    const code = `(() => "${expected}")()`
-    const asyncCode = `(() => new Promise(r => setTimeout(() => r("${expected}"), 500)))()`
-    const badAsyncCode = `(() => new Promise((r, e) => setTimeout(() => e("${expectedErrorMsg}"), 500)))()`
-    const errorTypes = new Set([
-      Error,
-      ReferenceError,
-      EvalError,
-      RangeError,
-      SyntaxError,
-      TypeError,
-      URIError
-    ])
-
-    it('resolves the returned promise with the result', (done) => {
-      ipcRenderer.send('executeJavaScript', code)
-      ipcRenderer.once('executeJavaScript-promise-response', (event, result) => {
-        expect(result).to.equal(expected)
-        done()
-      })
-    })
-    it('resolves the returned promise with the result if the code returns an asyncronous promise', (done) => {
-      ipcRenderer.send('executeJavaScript', asyncCode)
-      ipcRenderer.once('executeJavaScript-promise-response', (event, result) => {
-        expect(result).to.equal(expected)
-        done()
-      })
-    })
-    it('rejects the returned promise if an async error is thrown', (done) => {
-      ipcRenderer.send('executeJavaScript', badAsyncCode)
-      ipcRenderer.once('executeJavaScript-promise-error', (event, error) => {
-        expect(error).to.equal(expectedErrorMsg)
-        done()
-      })
-    })
-    it('rejects the returned promise with an error if an Error.prototype is thrown', async () => {
-      for (const error in errorTypes) {
-        await new Promise((resolve) => {
-          ipcRenderer.send('executeJavaScript', `Promise.reject(new ${error.name}("Wamp-wamp")`)
-          ipcRenderer.once('executeJavaScript-promise-error-name', (event, name) => {
-            expect(name).to.equal(error.name)
-            resolve()
-          })
-        })
-      }
-    })
-
-    it('works after page load and during subframe load', (done) => {
-      w.webContents.once('did-finish-load', () => {
-        // initiate a sub-frame load, then try and execute script during it
-        w.webContents.executeJavaScript(`
-          var iframe = document.createElement('iframe')
-          iframe.src = '${server.url}/slow'
-          document.body.appendChild(iframe)
-        `).then(() => {
-          w.webContents.executeJavaScript('console.log(\'hello\')').then(() => {
-            done()
-          })
-        })
-      })
-      w.loadURL(server.url)
-    })
-
-    it('executes after page load', (done) => {
-      w.webContents.executeJavaScript(code).then(result => {
-        expect(result).to.equal(expected)
-        done()
-      })
-      w.loadURL(server.url)
-    })
-
-    it('works with result objects that have DOM class prototypes', (done) => {
-      w.webContents.executeJavaScript('document.location').then(result => {
-        expect(result.origin).to.equal(server.url)
-        expect(result.protocol).to.equal('http:')
-        done()
-      })
-      w.loadURL(server.url)
-    })
-  })
-
-  describe('previewFile', () => {
-    before(function () {
-      if (process.platform !== 'darwin') {
-        this.skip()
-      }
-    })
-
-    it('opens the path in Quick Look on macOS', () => {
-      expect(() => {
-        w.previewFile(__filename)
-        w.closeFilePreview()
-      }).to.not.throw()
-    })
-  })
-
-  describe('contextIsolation option with and without sandbox option', () => {
-    const expectedContextData = {
-      preloadContext: {
-        preloadProperty: 'number',
-        pageProperty: 'undefined',
-        typeofRequire: 'function',
-        typeofProcess: 'object',
-        typeofArrayPush: 'function',
-        typeofFunctionApply: 'function',
-        typeofPreloadExecuteJavaScriptProperty: 'undefined'
-      },
-      pageContext: {
-        preloadProperty: 'undefined',
-        pageProperty: 'string',
-        typeofRequire: 'undefined',
-        typeofProcess: 'undefined',
-        typeofArrayPush: 'number',
-        typeofFunctionApply: 'boolean',
-        typeofPreloadExecuteJavaScriptProperty: 'number',
-        typeofOpenedWindow: 'object'
-      }
-    }
-
-    beforeEach(() => {
-      if (iw != null) iw.destroy()
-      iw = new BrowserWindow({
-        show: false,
-        webPreferences: {
-          contextIsolation: true,
-          preload: path.join(fixtures, 'api', 'isolated-preload.js')
-        }
-      })
-      if (ws != null) ws.destroy()
-      ws = new BrowserWindow({
-        show: false,
-        webPreferences: {
-          sandbox: true,
-          contextIsolation: true,
-          preload: path.join(fixtures, 'api', 'isolated-preload.js')
-        }
-      })
-    })
-
-    afterEach(() => {
-      if (iw != null) iw.destroy()
-      if (ws != null) ws.destroy()
-    })
-
-    it('separates the page context from the Electron/preload context', async () => {
-      const p = emittedOnce(ipcMain, 'isolated-world')
-      iw.loadFile(path.join(fixtures, 'api', 'isolated.html'))
-      const [, data] = await p
-      expect(data).to.deep.equal(expectedContextData)
-    })
-    it('recreates the contexts on reload', async () => {
-      await iw.loadFile(path.join(fixtures, 'api', 'isolated.html'))
-      const isolatedWorld = emittedOnce(ipcMain, 'isolated-world')
-      iw.webContents.reload()
-      const [, data] = await isolatedWorld
-      expect(data).to.deep.equal(expectedContextData)
-    })
-    it('enables context isolation on child windows', async () => {
-      const browserWindowCreated = emittedOnce(app, 'browser-window-created')
-      iw.loadFile(path.join(fixtures, 'pages', 'window-open.html'))
-      const [, window] = await browserWindowCreated
-      expect(window.webContents.getLastWebPreferences().contextIsolation).to.be.true()
-    })
-    it('separates the page context from the Electron/preload context with sandbox on', async () => {
-      const p = emittedOnce(ipcMain, 'isolated-world')
-      ws.loadFile(path.join(fixtures, 'api', 'isolated.html'))
-      const [, data] = await p
-      expect(data).to.deep.equal(expectedContextData)
-    })
-    it('recreates the contexts on reload with sandbox on', async () => {
-      await ws.loadFile(path.join(fixtures, 'api', 'isolated.html'))
-      const isolatedWorld = emittedOnce(ipcMain, 'isolated-world')
-      ws.webContents.reload()
-      const [, data] = await isolatedWorld
-      expect(data).to.deep.equal(expectedContextData)
-    })
-    it('supports fetch api', async () => {
-      const fetchWindow = new BrowserWindow({
-        show: false,
-        webPreferences: {
-          contextIsolation: true,
-          preload: path.join(fixtures, 'api', 'isolated-fetch-preload.js')
-        }
-      })
-      const p = emittedOnce(ipcMain, 'isolated-fetch-error')
-      fetchWindow.loadURL('about:blank')
-      const [, error] = await p
-      fetchWindow.destroy()
-      expect(error).to.equal('Failed to fetch')
-    })
-    it('doesn\'t break ipc serialization', async () => {
-      const p = emittedOnce(ipcMain, 'isolated-world')
-      iw.loadURL('about:blank')
-      iw.webContents.executeJavaScript(`
-        const opened = window.open()
-        openedLocation = opened.location.href
-        opened.close()
-        window.postMessage({openedLocation}, '*')
-      `)
-      const [, data] = await p
-      expect(data.pageContext.openedLocation).to.equal('')
-    })
-  })
-
-  describe('offscreen rendering', () => {
-    beforeEach(function () {
-      if (!features.isOffscreenRenderingEnabled()) {
-        // XXX(alexeykuzmin): "afterEach" hook is not called
-        // for skipped tests, we have to close the window manually.
-        return closeTheWindow().then(() => { this.skip() })
-      }
-
-      if (w != null) w.destroy()
-      w = new BrowserWindow({
-        width: 100,
-        height: 100,
-        show: false,
-        webPreferences: {
-          backgroundThrottling: false,
-          offscreen: true
-        }
-      })
-    })
-
-    it('creates offscreen window with correct size', (done) => {
-      w.webContents.once('paint', function (event, rect, data) {
-        expect(data.constructor.name).to.equal('NativeImage')
-        expect(data.isEmpty()).to.be.false()
-        const size = data.getSize()
-        expect(size.width).to.be.closeTo(100 * devicePixelRatio, 2)
-        expect(size.height).to.be.closeTo(100 * devicePixelRatio, 2)
-        done()
-      })
-      w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-    })
-
-    it('does not crash after navigation', () => {
-      w.webContents.loadURL('about:blank')
-      w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-    })
-
-    describe('window.webContents.isOffscreen()', () => {
-      it('is true for offscreen type', () => {
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-        expect(w.webContents.isOffscreen()).to.be.true()
-      })
-
-      it('is false for regular window', () => {
-        const c = new BrowserWindow({ show: false })
-        expect(c.webContents.isOffscreen()).to.be.false()
-        c.destroy()
-      })
-    })
-
-    describe('window.webContents.isPainting()', () => {
-      it('returns whether is currently painting', (done) => {
-        w.webContents.once('paint', function (event, rect, data) {
-          expect(w.webContents.isPainting()).to.be.true()
-          done()
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-    })
-
-    describe('window.webContents.stopPainting()', () => {
-      it('stops painting', (done) => {
-        w.webContents.on('dom-ready', () => {
-          w.webContents.stopPainting()
-          expect(w.webContents.isPainting()).to.be.false()
-          done()
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-    })
-
-    describe('window.webContents.startPainting()', () => {
-      it('starts painting', (done) => {
-        w.webContents.on('dom-ready', () => {
-          w.webContents.stopPainting()
-          w.webContents.startPainting()
-          w.webContents.once('paint', function (event, rect, data) {
-            expect(w.webContents.isPainting()).to.be.true()
-            done()
-          })
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-    })
-
-    // TODO(codebytere): remove in Electron v8.0.0
-    describe('window.webContents.getFrameRate()', () => {
-      it('has default frame rate', (done) => {
-        w.webContents.once('paint', function (event, rect, data) {
-          expect(w.webContents.getFrameRate()).to.equal(60)
-          done()
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-    })
-
-    // TODO(codebytere): remove in Electron v8.0.0
-    describe('window.webContents.setFrameRate(frameRate)', () => {
-      it('sets custom frame rate', (done) => {
-        w.webContents.on('dom-ready', () => {
-          w.webContents.setFrameRate(30)
-          w.webContents.once('paint', function (event, rect, data) {
-            expect(w.webContents.getFrameRate()).to.equal(30)
-            done()
-          })
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-    })
-
-    describe('window.webContents.FrameRate', () => {
-      it('has default frame rate', (done) => {
-        w.webContents.once('paint', function (event, rect, data) {
-          expect(w.webContents.frameRate).to.equal(60)
-          done()
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-
-      it('sets custom frame rate', (done) => {
-        w.webContents.on('dom-ready', () => {
-          w.webContents.frameRate = 30
-          w.webContents.once('paint', function (event, rect, data) {
-            expect(w.webContents.frameRate).to.equal(30)
-            done()
-          })
-        })
-        w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'))
-      })
-    })
-  })
-})
-
-const expectBoundsEqual = (actual, expected) => {
-  if (!isScaleFactorRounding()) {
-    expect(expected).to.deep.equal(actual)
-  } else if (Array.isArray(actual)) {
-    expect(actual[0]).to.be.closeTo(expected[0], 1)
-    expect(actual[1]).to.be.closeTo(expected[1], 1)
-  } else {
-    expect(actual.x).to.be.closeTo(expected.x, 1)
-    expect(actual.y).to.be.closeTo(expected.y, 1)
-    expect(actual.width).to.be.closeTo(expected.width, 1)
-    expect(actual.height).to.be.closeTo(expected.height, 1)
-  }
-}
-
-// Is the display's scale factor possibly causing rounding of pixel coordinate
-// values?
-const isScaleFactorRounding = () => {
-  const { scaleFactor } = screen.getPrimaryDisplay()
-  // Return true if scale factor is non-integer value
-  if (Math.round(scaleFactor) !== scaleFactor) return true
-  // Return true if scale factor is odd number above 2
-  return scaleFactor > 2 && scaleFactor % 2 === 1
-}

+ 0 - 20
spec/static/main.js

@@ -159,18 +159,6 @@ app.on('ready', function () {
     })
     event.returnValue = null
   })
-
-  ipcMain.on('executeJavaScript', function (event, code) {
-    window.webContents.executeJavaScript(code).then((result) => {
-      window.webContents.send('executeJavaScript-promise-response', result)
-    }).catch((error) => {
-      window.webContents.send('executeJavaScript-promise-error', error)
-
-      if (error && error.name) {
-        window.webContents.send('executeJavaScript-promise-error-name', error.name)
-      }
-    })
-  })
 })
 
 ipcMain.on('handle-next-ipc-message-sync', function (event, returnValue) {
@@ -224,18 +212,10 @@ ipcMain.on('create-window-with-options-cycle', (event) => {
   event.returnValue = window.id
 })
 
-ipcMain.on('prevent-next-new-window', (event, id) => {
-  webContents.fromId(id).once('new-window', event => event.preventDefault())
-})
-
 ipcMain.on('prevent-next-will-attach-webview', (event) => {
   event.sender.once('will-attach-webview', event => event.preventDefault())
 })
 
-ipcMain.on('prevent-next-will-prevent-unload', (event, id) => {
-  webContents.fromId(id).once('will-prevent-unload', event => event.preventDefault())
-})
-
 ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => {
   event.sender.once('will-attach-webview', (event, webPreferences, params) => {
     params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}`