From 81766707fcc2f234785e1907c25c094a4a27adb7 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Mon, 15 Aug 2022 01:06:02 -0700 Subject: [PATCH] test: migrate remaining webview specs to main (#35213) * test: migrate remaining webview specs to main * fix merge * add dirty-chai * retry capturePage() * fix test --- spec-main/webview-spec.ts | 421 ++++++++++++++++++++++++++++++++++++- spec/webview-spec.js | 431 -------------------------------------- 2 files changed, 420 insertions(+), 432 deletions(-) delete mode 100644 spec/webview-spec.js diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index 444a9cea618b..a587fe7a719a 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -3,12 +3,14 @@ import * as url from 'url'; import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main'; import { closeAllWindows } from './window-helpers'; import { emittedOnce, emittedUntil } from './events-helpers'; -import { ifit, delay, defer, itremote, useRemoteContext } from './spec-helpers'; +import { ifit, ifdescribe, delay, defer, itremote, useRemoteContext } from './spec-helpers'; import { expect } from 'chai'; import * as http from 'http'; import { AddressInfo } from 'net'; +import * as auth from 'basic-auth'; declare let WebView: any; +const features = process._linkedBinding('electron_common_features'); async function loadWebView (w: WebContents, attributes: Record, opts?: {openDevTools?: boolean}): Promise { const { openDevTools } = { @@ -1699,5 +1701,422 @@ describe(' tag', function () { expect(message).to.equal('undefined'); }); }); + + describe('media-started-playing and media-paused events', () => { + it('emits when audio starts and stops playing', async function () { + if (!await w.executeJavaScript('document.createElement(\'audio\').canPlayType(\'audio/wav\')')) { return this.skip(); } + + await loadWebView(w, { src: blankPageUrl }); + + // With the new autoplay policy, audio elements must be unmuted + // see https://goo.gl/xX8pDD. + await w.executeJavaScript(`new Promise(resolve => { + webview.executeJavaScript(\` + const audio = document.createElement("audio") + audio.src = "../assets/tone.wav" + document.body.appendChild(audio); + audio.play() + \`, true) + webview.addEventListener('media-started-playing', () => resolve(), {once: true}) + })`); + + await w.executeJavaScript(`new Promise(resolve => { + webview.executeJavaScript(\` + document.querySelector("audio").pause() + \`, true) + webview.addEventListener('media-paused', () => resolve(), {once: true}) + })`); + }); + }); + }); + + describe('methods', () => { + let w: WebContents; + before(async () => { + const window = new BrowserWindow({ + show: false, + webPreferences: { + webviewTag: true, + nodeIntegration: true, + contextIsolation: false + } + }); + await window.loadURL(`file://${fixtures}/pages/blank.html`); + w = window.webContents; + }); + afterEach(async () => { + await w.executeJavaScript(`{ + document.querySelectorAll('webview').forEach(el => el.remove()) + }`); + }); + after(closeAllWindows); + + describe('.reload()', () => { + it('should emit beforeunload handler', async () => { + await loadWebView(w, { + nodeintegration: 'on', + webpreferences: 'contextIsolation=no', + src: `file://${fixtures}/pages/beforeunload-false.html` + }); + + // Event handler has to be added before reload. + const channel = await w.executeJavaScript(`new Promise(resolve => { + webview.addEventListener('ipc-message', e => resolve(e.channel)) + webview.reload(); + })`); + + expect(channel).to.equal('onbeforeunload'); + }); + }); + + describe('.goForward()', () => { + useRemoteContext({ webPreferences: { webviewTag: true } }); + itremote('should work after a replaced history entry', async (fixtures: string) => { + const webview = new WebView(); + + webview.setAttribute('nodeintegration', 'on'); + webview.setAttribute('webpreferences', 'contextIsolation=no'); + webview.src = `file://${fixtures}/pages/history-replace.html`; + document.body.appendChild(webview); + + { + const [e] = await Promise.all([ + new Promise(resolve => webview.addEventListener('ipc-message', resolve, { once: true })), + new Promise(resolve => webview.addEventListener('did-stop-loading', resolve, { once: true })) + ]); + expect(e.channel).to.equal('history'); + expect(e.args[0]).to.equal(1); + expect(webview.canGoBack()).to.be.false(); + expect(webview.canGoForward()).to.be.false(); + } + + webview.src = `file://${fixtures}/pages/base-page.html`; + + await new Promise(resolve => webview.addEventListener('did-stop-loading', resolve, { once: true })); + + expect(webview.canGoBack()).to.be.true(); + expect(webview.canGoForward()).to.be.false(); + + webview.goBack(); + + { + const [e] = await Promise.all([ + new Promise(resolve => webview.addEventListener('ipc-message', resolve, { once: true })), + new Promise(resolve => webview.addEventListener('did-stop-loading', resolve, { once: true })) + ]); + expect(e.channel).to.equal('history'); + expect(e.args[0]).to.equal(2); + expect(webview.canGoBack()).to.be.false(); + expect(webview.canGoForward()).to.be.true(); + } + + webview.goForward(); + + await new Promise(resolve => webview.addEventListener('did-stop-loading', resolve, { once: true })); + + expect(webview.canGoBack()).to.be.true(); + expect(webview.canGoForward()).to.be.false(); + }, [fixtures]); + }); + + describe('.clearHistory()', () => { + it('should clear the navigation history', async () => { + await loadWebView(w, { + nodeintegration: 'on', + webpreferences: 'contextIsolation=no', + src: blankPageUrl + }); + + // Navigation must be triggered by a user gesture to make canGoBack() return true + await w.executeJavaScript('webview.executeJavaScript(`history.pushState(null, "", "foo.html")`, true)'); + + expect(await w.executeJavaScript('webview.canGoBack()')).to.be.true(); + + await w.executeJavaScript('webview.clearHistory()'); + expect(await w.executeJavaScript('webview.canGoBack()')).to.be.false(); + }); + }); + + describe('executeJavaScript', () => { + it('can return the result of the executed script', async () => { + await loadWebView(w, { + src: 'about:blank' + }); + + const jsScript = "'4'+2"; + const expectedResult = '42'; + + const result = await w.executeJavaScript(`webview.executeJavaScript(${JSON.stringify(jsScript)})`); + expect(result).to.equal(expectedResult); + }); + }); + + it('supports inserting CSS', async () => { + await loadWebView(w, { src: `file://${fixtures}/pages/base-page.html` }); + await w.executeJavaScript('webview.insertCSS(\'body { background-repeat: round; }\')'); + const result = await w.executeJavaScript('webview.executeJavaScript(\'window.getComputedStyle(document.body).getPropertyValue("background-repeat")\')'); + expect(result).to.equal('round'); + }); + + it('supports removing inserted CSS', async () => { + await loadWebView(w, { src: `file://${fixtures}/pages/base-page.html` }); + const key = await w.executeJavaScript('webview.insertCSS(\'body { background-repeat: round; }\')'); + await w.executeJavaScript(`webview.removeInsertedCSS(${JSON.stringify(key)})`); + const result = await w.executeJavaScript('webview.executeJavaScript(\'window.getComputedStyle(document.body).getPropertyValue("background-repeat")\')'); + expect(result).to.equal('repeat'); + }); + + describe('sendInputEvent', () => { + it('can send keyboard event', async () => { + await loadWebViewAndWaitForEvent(w, { + nodeintegration: 'on', + webpreferences: 'contextIsolation=no', + src: `file://${fixtures}/pages/onkeyup.html` + }, 'dom-ready'); + + const waitForIpcMessage = w.executeJavaScript('new Promise(resolve => webview.addEventListener("ipc-message", e => resolve({...e})), {once: true})'); + w.executeJavaScript(`webview.sendInputEvent({ + type: 'keyup', + keyCode: 'c', + modifiers: ['shift'] + })`); + + const { channel, args } = await waitForIpcMessage; + expect(channel).to.equal('keyup'); + expect(args).to.deep.equal(['C', 'KeyC', 67, true, false]); + }); + + it('can send mouse event', async () => { + await loadWebViewAndWaitForEvent(w, { + nodeintegration: 'on', + webpreferences: 'contextIsolation=no', + src: `file://${fixtures}/pages/onmouseup.html` + }, 'dom-ready'); + + const waitForIpcMessage = w.executeJavaScript('new Promise(resolve => webview.addEventListener("ipc-message", e => resolve({...e})), {once: true})'); + w.executeJavaScript(`webview.sendInputEvent({ + type: 'mouseup', + modifiers: ['ctrl'], + x: 10, + y: 20 + })`); + + const { channel, args } = await waitForIpcMessage; + expect(channel).to.equal('mouseup'); + expect(args).to.deep.equal([10, 20, false, true]); + }); + }); + + describe('.getWebContentsId', () => { + it('can return the WebContents ID', async () => { + await loadWebView(w, { src: 'about:blank' }); + + expect(await w.executeJavaScript('webview.getWebContentsId()')).to.be.a('number'); + }); + }); + + ifdescribe(features.isPrintingEnabled())('.printToPDF()', () => { + it('rejects on incorrectly typed parameters', async () => { + const badTypes = { + landscape: [], + displayHeaderFooter: '123', + printBackground: 2, + scale: 'not-a-number', + pageSize: 'IAmAPageSize', + margins: 'terrible', + pageRanges: { oops: 'im-not-the-right-key' }, + headerTemplate: [1, 2, 3], + footerTemplate: [4, 5, 6], + preferCSSPageSize: 'no' + }; + + // These will hard crash in Chromium unless we type-check + for (const [key, value] of Object.entries(badTypes)) { + const param = { [key]: value }; + + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(w, { src }); + await expect(w.executeJavaScript(`webview.printToPDF(${JSON.stringify(param)})`)).to.eventually.be.rejected(); + } + }); + + it('can print to PDF', async () => { + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(w, { src }); + + const data = await w.executeJavaScript('webview.printToPDF({})'); + expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty(); + }); + }); + + describe('DOM events', () => { + /* + let div; + + beforeEach(() => { + div = document.createElement('div'); + div.style.width = '100px'; + div.style.height = '10px'; + div.style.overflow = 'hidden'; + webview.style.height = '100%'; + webview.style.width = '100%'; + }); + + afterEach(() => { + if (div != null) div.remove(); + }); + */ + + for (const [description, sandbox] of [ + ['without sandbox', false] as const, + ['with sandbox', true] as const + ]) { + describe(description, () => { + // TODO(nornagon): disabled during chromium roll 2019-06-11 due to a + // 'ResizeObserver loop limit exceeded' error on Windows + /* + xit('emits resize events', async () => { + const firstResizeSignal = waitForEvent(webview, 'resize'); + const domReadySignal = waitForEvent(webview, 'dom-ready'); + + webview.src = `file://${fixtures}/pages/a.html`; + webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; + div.appendChild(webview); + document.body.appendChild(div); + + const firstResizeEvent = await firstResizeSignal; + expect(firstResizeEvent.target).to.equal(webview); + expect(firstResizeEvent.newWidth).to.equal(100); + expect(firstResizeEvent.newHeight).to.equal(10); + + await domReadySignal; + + const secondResizeSignal = waitForEvent(webview, 'resize'); + + const newWidth = 1234; + const newHeight = 789; + div.style.width = `${newWidth}px`; + div.style.height = `${newHeight}px`; + + const secondResizeEvent = await secondResizeSignal; + expect(secondResizeEvent.target).to.equal(webview); + expect(secondResizeEvent.newWidth).to.equal(newWidth); + expect(secondResizeEvent.newHeight).to.equal(newHeight); + }); + */ + + it('emits focus event', async () => { + await loadWebViewAndWaitForEvent(w, { + src: `file://${fixtures}/pages/a.html`, + webpreferences: `sandbox=${sandbox ? 'yes' : 'no'}` + }, 'dom-ready'); + + // If this test fails, check if webview.focus() still works. + await w.executeJavaScript(`new Promise(resolve => { + webview.addEventListener('focus', () => resolve(), {once: true}); + webview.focus(); + })`); + }); + }); + } + }); + + // TODO(miniak): figure out why this is failing on windows + ifdescribe(process.platform !== 'win32')('.capturePage()', () => { + it('returns a Promise with a NativeImage', async () => { + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebViewAndWaitForEvent(w, { src }, 'did-stop-loading'); + + // Retry a few times due to flake. + for (let i = 0; i < 5; i++) { + try { + const image = await w.executeJavaScript('webview.capturePage()'); + const imgBuffer = image.toPNG(); + + // Check the 25th byte in the PNG. + // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha + expect(imgBuffer[25]).to.equal(6); + return; + } catch (e) { + /* drop the error */ + } + } + expect(false).to.be.true('could not successfully capture the page'); + }); + }); + + // FIXME(zcbenz): Disabled because of moving to OOPIF webview. + xdescribe('setDevToolsWebContents() API', () => { + /* + it('sets webContents of webview as devtools', async () => { + const webview2 = new WebView(); + loadWebView(webview2); + + // Setup an event handler for further usage. + const waitForDomReady = waitForEvent(webview2, 'dom-ready'); + + loadWebView(webview, { src: 'about:blank' }); + await waitForEvent(webview, 'dom-ready'); + webview.getWebContents().setDevToolsWebContents(webview2.getWebContents()); + webview.getWebContents().openDevTools(); + + await waitForDomReady; + + // Its WebContents should be a DevTools. + const devtools = webview2.getWebContents(); + expect(devtools.getURL().startsWith('devtools://devtools')).to.be.true(); + + const name = await devtools.executeJavaScript('InspectorFrontendHost.constructor.name'); + document.body.removeChild(webview2); + + expect(name).to.be.equal('InspectorFrontendHostImpl'); + }); + */ + }); + }); + + describe('basic auth', () => { + let w: WebContents; + before(async () => { + const window = new BrowserWindow({ + show: false, + webPreferences: { + webviewTag: true, + nodeIntegration: true, + contextIsolation: false + } + }); + await window.loadURL(`file://${fixtures}/pages/blank.html`); + w = window.webContents; + }); + afterEach(async () => { + await w.executeJavaScript(`{ + document.querySelectorAll('webview').forEach(el => el.remove()) + }`); + }); + after(closeAllWindows); + + it('should authenticate with correct credentials', async () => { + const message = 'Authenticated'; + const server = http.createServer((req, res) => { + const credentials = auth(req)!; + if (credentials.name === 'test' && credentials.pass === 'test') { + res.end(message); + } else { + res.end('failed'); + } + }); + defer(() => { + server.close(); + }); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); + const port = (server.address() as AddressInfo).port; + const e = await loadWebViewAndWaitForEvent(w, { + nodeintegration: 'on', + webpreferences: 'contextIsolation=no', + src: `file://${fixtures}/pages/basic-auth.html?port=${port}` + }, 'ipc-message'); + expect(e.channel).to.equal(message); + }); }); }); diff --git a/spec/webview-spec.js b/spec/webview-spec.js deleted file mode 100644 index 226b7c670db4..000000000000 --- a/spec/webview-spec.js +++ /dev/null @@ -1,431 +0,0 @@ -const { expect } = require('chai'); -const path = require('path'); -const http = require('http'); -const url = require('url'); -const { ipcRenderer } = require('electron'); -const { emittedOnce, waitForEvent } = require('./events-helpers'); -const { ifdescribe, ifit, delay } = require('./spec-helpers'); - -const features = process._linkedBinding('electron_common_features'); -const nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; - -/* Most of the APIs here don't use standard callbacks */ -/* eslint-disable standard/no-callback-literal */ - -describe(' tag', function () { - this.timeout(3 * 60 * 1000); - - const fixtures = path.join(__dirname, 'fixtures'); - let webview = null; - - const loadWebView = async (webview, attributes = {}) => { - for (const [name, value] of Object.entries(attributes)) { - webview.setAttribute(name, value); - } - document.body.appendChild(webview); - await waitForEvent(webview, 'did-finish-load'); - return webview; - }; - - const startLoadingWebViewAndWaitForMessage = async (webview, attributes = {}) => { - loadWebView(webview, attributes); // Don't wait for load to be finished. - const event = await waitForEvent(webview, 'console-message'); - return event.message; - }; - - beforeEach(() => { - webview = new WebView(); - }); - - afterEach(() => { - if (!document.body.contains(webview)) { - document.body.appendChild(webview); - } - webview.remove(); - }); - - // FIXME(zcbenz): Disabled because of moving to OOPIF webview. - xdescribe('setDevToolsWebContents() API', () => { - it('sets webContents of webview as devtools', async () => { - const webview2 = new WebView(); - loadWebView(webview2); - - // Setup an event handler for further usage. - const waitForDomReady = waitForEvent(webview2, 'dom-ready'); - - loadWebView(webview, { src: 'about:blank' }); - await waitForEvent(webview, 'dom-ready'); - webview.getWebContents().setDevToolsWebContents(webview2.getWebContents()); - webview.getWebContents().openDevTools(); - - await waitForDomReady; - - // Its WebContents should be a DevTools. - const devtools = webview2.getWebContents(); - expect(devtools.getURL().startsWith('devtools://devtools')).to.be.true(); - - const name = await devtools.executeJavaScript('InspectorFrontendHost.constructor.name'); - document.body.removeChild(webview2); - - expect(name).to.be.equal('InspectorFrontendHostImpl'); - }); - }); - - describe('.reload()', () => { - it('should emit beforeunload handler', async () => { - await loadWebView(webview, { - nodeintegration: 'on', - webpreferences: 'contextIsolation=no', - src: `file://${fixtures}/pages/beforeunload-false.html` - }); - - // Event handler has to be added before reload. - const waitForOnbeforeunload = waitForEvent(webview, 'ipc-message'); - - webview.reload(); - - const { channel } = await waitForOnbeforeunload; - expect(channel).to.equal('onbeforeunload'); - }); - }); - - describe('.goForward()', () => { - it('should work after a replaced history entry', (done) => { - let loadCount = 1; - const listener = (e) => { - if (loadCount === 1) { - expect(e.channel).to.equal('history'); - expect(e.args[0]).to.equal(1); - expect(webview.canGoBack()).to.be.false(); - expect(webview.canGoForward()).to.be.false(); - } else if (loadCount === 2) { - expect(e.channel).to.equal('history'); - expect(e.args[0]).to.equal(2); - expect(webview.canGoBack()).to.be.false(); - expect(webview.canGoForward()).to.be.true(); - webview.removeEventListener('ipc-message', listener); - } - }; - - const loadListener = () => { - try { - if (loadCount === 1) { - webview.src = `file://${fixtures}/pages/base-page.html`; - } else if (loadCount === 2) { - expect(webview.canGoBack()).to.be.true(); - expect(webview.canGoForward()).to.be.false(); - - webview.goBack(); - } else if (loadCount === 3) { - webview.goForward(); - } else if (loadCount === 4) { - expect(webview.canGoBack()).to.be.true(); - expect(webview.canGoForward()).to.be.false(); - - webview.removeEventListener('did-finish-load', loadListener); - done(); - } - - loadCount += 1; - } catch (e) { - done(e); - } - }; - - webview.addEventListener('ipc-message', listener); - webview.addEventListener('did-finish-load', loadListener); - - loadWebView(webview, { - nodeintegration: 'on', - src: `file://${fixtures}/pages/history-replace.html` - }); - }); - }); - - // FIXME: https://github.com/electron/electron/issues/19397 - xdescribe('.clearHistory()', () => { - it('should clear the navigation history', async () => { - const message = waitForEvent(webview, 'ipc-message'); - await loadWebView(webview, { - nodeintegration: 'on', - src: `file://${fixtures}/pages/history.html` - }); - const event = await message; - - expect(event.channel).to.equal('history'); - expect(event.args[0]).to.equal(2); - expect(webview.canGoBack()).to.be.true(); - - webview.clearHistory(); - expect(webview.canGoBack()).to.be.false(); - }); - }); - - describe('basic auth', () => { - const auth = require('basic-auth'); - - it('should authenticate with correct credentials', (done) => { - const message = 'Authenticated'; - const server = http.createServer((req, res) => { - const credentials = auth(req); - if (credentials.name === 'test' && credentials.pass === 'test') { - res.end(message); - } else { - res.end('failed'); - } - server.close(); - }); - server.listen(0, '127.0.0.1', () => { - const port = server.address().port; - webview.addEventListener('ipc-message', (e) => { - try { - expect(e.channel).to.equal(message); - done(); - } catch (e) { - done(e); - } - }); - loadWebView(webview, { - nodeintegration: 'on', - webpreferences: 'contextIsolation=no', - src: `file://${fixtures}/pages/basic-auth.html?port=${port}` - }); - }); - }); - }); - - describe('executeJavaScript', () => { - it('can return the result of the executed script', async () => { - await loadWebView(webview, { - src: 'about:blank' - }); - - const jsScript = "'4'+2"; - const expectedResult = '42'; - - const result = await webview.executeJavaScript(jsScript); - expect(result).to.equal(expectedResult); - }); - }); - - it('supports inserting CSS', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); - await webview.insertCSS('body { background-repeat: round; }'); - const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); - expect(result).to.equal('round'); - }); - - it('supports removing inserted CSS', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); - const key = await webview.insertCSS('body { background-repeat: round; }'); - await webview.removeInsertedCSS(key); - const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); - expect(result).to.equal('repeat'); - }); - - describe('sendInputEvent', () => { - it('can send keyboard event', async () => { - loadWebView(webview, { - nodeintegration: 'on', - webpreferences: 'contextIsolation=no', - src: `file://${fixtures}/pages/onkeyup.html` - }); - await waitForEvent(webview, 'dom-ready'); - - const waitForIpcMessage = waitForEvent(webview, 'ipc-message'); - webview.sendInputEvent({ - type: 'keyup', - keyCode: 'c', - modifiers: ['shift'] - }); - - const { channel, args } = await waitForIpcMessage; - expect(channel).to.equal('keyup'); - expect(args).to.deep.equal(['C', 'KeyC', 67, true, false]); - }); - - it('can send mouse event', async () => { - loadWebView(webview, { - nodeintegration: 'on', - webpreferences: 'contextIsolation=no', - src: `file://${fixtures}/pages/onmouseup.html` - }); - await waitForEvent(webview, 'dom-ready'); - - const waitForIpcMessage = waitForEvent(webview, 'ipc-message'); - webview.sendInputEvent({ - type: 'mouseup', - modifiers: ['ctrl'], - x: 10, - y: 20 - }); - - const { channel, args } = await waitForIpcMessage; - expect(channel).to.equal('mouseup'); - expect(args).to.deep.equal([10, 20, false, true]); - }); - }); - - describe('media-started-playing media-paused events', () => { - beforeEach(function () { - if (!document.createElement('audio').canPlayType('audio/wav')) { - this.skip(); - } - }); - - it('emits when audio starts and stops playing', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); - - // With the new autoplay policy, audio elements must be unmuted - // see https://goo.gl/xX8pDD. - const source = ` - const audio = document.createElement("audio") - audio.src = "../assets/tone.wav" - document.body.appendChild(audio); - audio.play() - `; - webview.executeJavaScript(source, true); - await waitForEvent(webview, 'media-started-playing'); - - webview.executeJavaScript('document.querySelector("audio").pause()', true); - await waitForEvent(webview, 'media-paused'); - }); - }); - - describe('.getWebContentsId', () => { - it('can return the WebContents ID', async () => { - const src = 'about:blank'; - await loadWebView(webview, { src }); - - expect(webview.getWebContentsId()).to.be.a('number'); - }); - }); - - // TODO(nornagon): this seems to have become much less reliable as of - // https://github.com/electron/electron/pull/32419. Tracked at - // https://github.com/electron/electron/issues/32705. - describe.skip('.capturePage()', () => { - before(function () { - // TODO(miniak): figure out why this is failing on windows - if (process.platform === 'win32') { - this.skip(); - } - }); - - it('returns a Promise with a NativeImage', async () => { - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; - await loadWebView(webview, { src }); - - const image = await webview.capturePage(); - const imgBuffer = image.toPNG(); - - // Check the 25th byte in the PNG. - // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha - expect(imgBuffer[25]).to.equal(6); - }); - }); - - ifdescribe(features.isPrintingEnabled())('.printToPDF()', () => { - it('rejects on incorrectly typed parameters', async () => { - const badTypes = { - landscape: [], - displayHeaderFooter: '123', - printBackground: 2, - scale: 'not-a-number', - pageSize: 'IAmAPageSize', - margins: 'terrible', - pageRanges: { oops: 'im-not-the-right-key' }, - headerTemplate: [1, 2, 3], - footerTemplate: [4, 5, 6], - preferCSSPageSize: 'no' - }; - - // These will hard crash in Chromium unless we type-check - for (const [key, value] of Object.entries(badTypes)) { - const param = { [key]: value }; - - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; - await loadWebView(webview, { src }); - await expect(webview.printToPDF(param)).to.eventually.be.rejected(); - } - }); - - it('can print to PDF', async () => { - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; - await loadWebView(webview, { src }); - - const data = await webview.printToPDF({}); - expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty(); - }); - }); - - describe('DOM events', () => { - let div; - - beforeEach(() => { - div = document.createElement('div'); - div.style.width = '100px'; - div.style.height = '10px'; - div.style.overflow = 'hidden'; - webview.style.height = '100%'; - webview.style.width = '100%'; - }); - - afterEach(() => { - if (div != null) div.remove(); - }); - - const generateSpecs = (description, sandbox) => { - describe(description, () => { - // TODO(nornagon): disabled during chromium roll 2019-06-11 due to a - // 'ResizeObserver loop limit exceeded' error on Windows - xit('emits resize events', async () => { - const firstResizeSignal = waitForEvent(webview, 'resize'); - const domReadySignal = waitForEvent(webview, 'dom-ready'); - - webview.src = `file://${fixtures}/pages/a.html`; - webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; - div.appendChild(webview); - document.body.appendChild(div); - - const firstResizeEvent = await firstResizeSignal; - expect(firstResizeEvent.target).to.equal(webview); - expect(firstResizeEvent.newWidth).to.equal(100); - expect(firstResizeEvent.newHeight).to.equal(10); - - await domReadySignal; - - const secondResizeSignal = waitForEvent(webview, 'resize'); - - const newWidth = 1234; - const newHeight = 789; - div.style.width = `${newWidth}px`; - div.style.height = `${newHeight}px`; - - const secondResizeEvent = await secondResizeSignal; - expect(secondResizeEvent.target).to.equal(webview); - expect(secondResizeEvent.newWidth).to.equal(newWidth); - expect(secondResizeEvent.newHeight).to.equal(newHeight); - }); - - it('emits focus event', async () => { - const domReadySignal = waitForEvent(webview, 'dom-ready'); - webview.src = `file://${fixtures}/pages/a.html`; - webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; - document.body.appendChild(webview); - - await domReadySignal; - - // If this test fails, check if webview.focus() still works. - const focusSignal = waitForEvent(webview, 'focus'); - webview.focus(); - - await focusSignal; - }); - }); - }; - - generateSpecs('without sandbox', false); - generateSpecs('with sandbox', true); - }); -});