From 111941bd3ceaceed44985e22b00f4eab44a69a5d Mon Sep 17 00:00:00 2001 From: Sri Harsha Date: Fri, 5 Apr 2024 12:56:25 +0530 Subject: [PATCH] [JS] Implement fullPageScreenshot functionality for Firefox (#13301) --- javascript/node/selenium-webdriver/firefox.js | 13 ++ .../test/firefox/addon_test.js | 139 +++++++++++++++ .../test/firefox/contextSwitching_test.js | 69 ++++++++ .../test/firefox/full_page_screenshot_test.js | 51 ++++++ .../options_test.js} | 162 +++--------------- 5 files changed, 296 insertions(+), 138 deletions(-) create mode 100644 javascript/node/selenium-webdriver/test/firefox/addon_test.js create mode 100644 javascript/node/selenium-webdriver/test/firefox/contextSwitching_test.js create mode 100644 javascript/node/selenium-webdriver/test/firefox/full_page_screenshot_test.js rename javascript/node/selenium-webdriver/test/{firefox_test.js => firefox/options_test.js} (54%) diff --git a/javascript/node/selenium-webdriver/firefox.js b/javascript/node/selenium-webdriver/firefox.js index 4190ddb4d5007..c38efd1b56d7f 100644 --- a/javascript/node/selenium-webdriver/firefox.js +++ b/javascript/node/selenium-webdriver/firefox.js @@ -444,6 +444,7 @@ const ExtensionCommand = { SET_CONTEXT: 'setContext', INSTALL_ADDON: 'install addon', UNINSTALL_ADDON: 'uninstall addon', + FULL_PAGE_SCREENSHOT: 'fullPage screenshot', } /** @@ -470,6 +471,8 @@ function configureExecutor(executor) { executor.defineCommand(ExtensionCommand.INSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/install') executor.defineCommand(ExtensionCommand.UNINSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/uninstall') + + executor.defineCommand(ExtensionCommand.FULL_PAGE_SCREENSHOT, 'GET', '/session/:sessionId/moz/screenshot/full') } /** @@ -645,6 +648,16 @@ class Driver extends webdriver.WebDriver { id = await Promise.resolve(id) return this.execute(new command.Command(ExtensionCommand.UNINSTALL_ADDON).setParameter('id', id)) } + + /** + * Take full page screenshot of the visible region + * + * @return {!Promise} A promise that will be + * resolved to the screenshot as a base-64 encoded PNG. + */ + takeFullPageScreenshot() { + return this.execute(new command.Command(ExtensionCommand.FULL_PAGE_SCREENSHOT)) + } } /** diff --git a/javascript/node/selenium-webdriver/test/firefox/addon_test.js b/javascript/node/selenium-webdriver/test/firefox/addon_test.js new file mode 100644 index 0000000000000..0afd23c75e1de --- /dev/null +++ b/javascript/node/selenium-webdriver/test/firefox/addon_test.js @@ -0,0 +1,139 @@ +/* + * Licensed to the Software Freedom Conservancy (SFC) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The SFC licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +'use strict' + +const assert = require('assert') +const { Browser } = require('../../index') +const { Pages, suite } = require('../../lib/test') +const { locate } = require('../../lib/test/resources') +const { until, By } = require('../../index') + +const EXT_XPI = locate('common/extensions/webextensions-selenium-example.xpi') +const EXT_UNSIGNED_ZIP = locate('common/extensions/webextensions-selenium-example-unsigned.zip') +const EXT_SIGNED_ZIP = locate('common/extensions/webextensions-selenium-example.zip') +const EXT_UNSIGNED_DIR = locate('common/extensions/webextensions-selenium-example') +const EXT_SIGNED_DIR = locate('common/extensions/webextensions-selenium-example') + +suite( + function (env) { + describe('firefox', function () { + let driver + + beforeEach(function () { + driver = null + }) + + afterEach(function () { + return driver && driver.quit() + }) + + describe('installAddon', function () { + beforeEach(function () { + driver = env.builder().build() + }) + + it('installs and uninstalls by xpi file', async function () { + await driver.get(Pages.blankPage) + await verifyWebExtensionNotInstalled() + + let id = await driver.installAddon(EXT_XPI) + + await driver.navigate().refresh() + await verifyWebExtensionWasInstalled() + + await driver.uninstallAddon(id) + await driver.navigate().refresh() + await verifyWebExtensionNotInstalled() + }) + + it('installs and uninstalls by unsigned zip file', async function () { + await driver.get(Pages.blankPage) + await verifyWebExtensionNotInstalled() + + let id = await driver.installAddon(EXT_UNSIGNED_ZIP, true) + + await driver.navigate().refresh() + await verifyWebExtensionWasInstalled() + + await driver.uninstallAddon(id) + await driver.navigate().refresh() + await verifyWebExtensionNotInstalled() + }) + + it('installs and uninstalls by signed zip file', async function () { + await driver.get(Pages.blankPage) + await verifyWebExtensionNotInstalled() + + let id = await driver.installAddon(EXT_SIGNED_ZIP) + + await driver.navigate().refresh() + await verifyWebExtensionWasInstalled() + + await driver.uninstallAddon(id) + await driver.navigate().refresh() + await verifyWebExtensionNotInstalled() + }) + + it('installs and uninstalls by unsigned directory', async function () { + await driver.get(Pages.blankPage) + await verifyWebExtensionNotInstalled() + + let id = await driver.installAddon(EXT_UNSIGNED_DIR, true) + + await driver.navigate().refresh() + await verifyWebExtensionWasInstalled() + + await driver.uninstallAddon(id) + await driver.navigate().refresh() + await verifyWebExtensionNotInstalled() + }) + + it('installs and uninstalls by signed directory', async function () { + await driver.get(Pages.blankPage) + await verifyWebExtensionNotInstalled() + + let id = await driver.installAddon(EXT_SIGNED_DIR, true) + + await driver.navigate().refresh() + await verifyWebExtensionWasInstalled() + + await driver.uninstallAddon(id) + await driver.navigate().refresh() + await verifyWebExtensionNotInstalled() + }) + }) + + async function verifyWebExtensionNotInstalled() { + let found = await driver.findElements({ + id: 'webextensions-selenium-example', + }) + assert.strictEqual(found.length, 0) + } + + async function verifyWebExtensionWasInstalled() { + let footer = await driver.wait(until.elementLocated(By.id('webextensions-selenium-example')), 5000) + + let text = await footer.getText() + assert.strictEqual(text, 'Content injected by webextensions-selenium-example') + } + }) + }, + { browsers: [Browser.FIREFOX] }, +) diff --git a/javascript/node/selenium-webdriver/test/firefox/contextSwitching_test.js b/javascript/node/selenium-webdriver/test/firefox/contextSwitching_test.js new file mode 100644 index 0000000000000..f8686d2ac8a09 --- /dev/null +++ b/javascript/node/selenium-webdriver/test/firefox/contextSwitching_test.js @@ -0,0 +1,69 @@ +/* + * Licensed to the Software Freedom Conservancy (SFC) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The SFC licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +'use strict' + +const assert = require('assert') +const error = require('../../lib/error') +const { Browser } = require('../../index') +const { Context } = require('../../firefox') +const { suite } = require('../../lib/test') + +suite( + function (env) { + describe('firefox', function () { + let driver + + beforeEach(function () { + driver = null + }) + + afterEach(function () { + return driver && driver.quit() + }) + + describe('context switching', function () { + beforeEach(async function () { + driver = await env.builder().build() + }) + + it('can get context', async function () { + assert.strictEqual(await driver.getContext(), Context.CONTENT) + }) + + it('can set context', async function () { + await driver.setContext(Context.CHROME) + let ctxt = await driver.getContext() + assert.strictEqual(ctxt, Context.CHROME) + + await driver.setContext(Context.CONTENT) + ctxt = await driver.getContext() + assert.strictEqual(ctxt, Context.CONTENT) + }) + + it('throws on unknown context', function () { + return driver.setContext('foo').then(assert.fail, function (e) { + assert(e instanceof error.InvalidArgumentError) + }) + }) + }) + }) + }, + { browsers: [Browser.FIREFOX] }, +) diff --git a/javascript/node/selenium-webdriver/test/firefox/full_page_screenshot_test.js b/javascript/node/selenium-webdriver/test/firefox/full_page_screenshot_test.js new file mode 100644 index 0000000000000..5659a58e2cad4 --- /dev/null +++ b/javascript/node/selenium-webdriver/test/firefox/full_page_screenshot_test.js @@ -0,0 +1,51 @@ +/* + * Licensed to the Software Freedom Conservancy (SFC) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The SFC licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +'use strict' + +const assert = require('assert') +const { Browser } = require('../../index') +const { Pages, suite } = require('../../lib/test') +let startIndex = 0 +let endIndex = 5 +let pngMagicNumber = 'iVBOR' +suite( + function (env) { + describe('firefox', function () { + let driver + + beforeEach(function () { + driver = null + }) + + afterEach(function () { + return driver && driver.quit() + }) + + it('Should be able to take full page screenshot', async function () { + driver = env.builder().build() + await driver.get(Pages.simpleTestPage) + let encoding = await driver.takeFullPageScreenshot() + const base64code = encoding.slice(startIndex, endIndex) + assert.equal(base64code, pngMagicNumber) + }) + }) + }, + { browsers: [Browser.FIREFOX] }, +) diff --git a/javascript/node/selenium-webdriver/test/firefox_test.js b/javascript/node/selenium-webdriver/test/firefox/options_test.js similarity index 54% rename from javascript/node/selenium-webdriver/test/firefox_test.js rename to javascript/node/selenium-webdriver/test/firefox/options_test.js index e7054d5e43391..4430c4169416a 100644 --- a/javascript/node/selenium-webdriver/test/firefox_test.js +++ b/javascript/node/selenium-webdriver/test/firefox/options_test.js @@ -1,39 +1,34 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. +/* + * Licensed to the Software Freedom Conservancy (SFC) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The SFC licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ 'use strict' const assert = require('assert') const path = require('path') -const error = require('../lib/error') -const firefox = require('../firefox') -const io = require('../io') -const { Browser } = require('../') -const { Context } = require('../firefox') -const { Pages, suite } = require('../lib/test') -const { locate } = require('../lib/test/resources') -const { until, By } = require('../index') +const firefox = require('../../firefox') +const io = require('../../io') +const { Browser } = require('../../index') +const { Pages, suite } = require('../../lib/test') +const { locate } = require('../../lib/test/resources') +const { until, By } = require('../../index') const EXT_XPI = locate('common/extensions/webextensions-selenium-example.xpi') -const EXT_UNSIGNED_ZIP = locate('common/extensions/webextensions-selenium-example-unsigned.zip') -const EXT_SIGNED_ZIP = locate('common/extensions/webextensions-selenium-example.zip') -const EXT_UNSIGNED_DIR = locate('common/extensions/webextensions-selenium-example') -const EXT_SIGNED_DIR = locate('common/extensions/webextensions-selenium-example') - const WEBEXTENSION_EXTENSION_ID = 'webextensions-selenium-example@example.com.xpi' suite( @@ -184,120 +179,11 @@ suite( }) }) - describe('context switching', function () { - beforeEach(async function () { - driver = await env.builder().build() - }) - - it('can get context', async function () { - assert.strictEqual(await driver.getContext(), Context.CONTENT) - }) - - it('can set context', async function () { - await driver.setContext(Context.CHROME) - let ctxt = await driver.getContext() - assert.strictEqual(ctxt, Context.CHROME) - - await driver.setContext(Context.CONTENT) - ctxt = await driver.getContext() - assert.strictEqual(ctxt, Context.CONTENT) - }) - - it('throws on unknown context', function () { - return driver.setContext('foo').then(assert.fail, function (e) { - assert(e instanceof error.InvalidArgumentError) - }) - }) - }) - - describe('installAddon', function () { - beforeEach(function () { - driver = env.builder().build() - }) - - it('installs and uninstalls by xpi file', async function () { - await driver.get(Pages.blankPage) - await verifyWebExtensionNotInstalled() - - let id = await driver.installAddon(EXT_XPI) - - await driver.navigate().refresh() - await verifyWebExtensionWasInstalled() - - await driver.uninstallAddon(id) - await driver.navigate().refresh() - await verifyWebExtensionNotInstalled() - }) - - it('installs and uninstalls by unsigned zip file', async function () { - await driver.get(Pages.blankPage) - await verifyWebExtensionNotInstalled() - - let id = await driver.installAddon(EXT_UNSIGNED_ZIP, true) - - await driver.navigate().refresh() - await verifyWebExtensionWasInstalled() - - await driver.uninstallAddon(id) - await driver.navigate().refresh() - await verifyWebExtensionNotInstalled() - }) - - it('installs and uninstalls by signed zip file', async function () { - await driver.get(Pages.blankPage) - await verifyWebExtensionNotInstalled() - - let id = await driver.installAddon(EXT_SIGNED_ZIP) - - await driver.navigate().refresh() - await verifyWebExtensionWasInstalled() - - await driver.uninstallAddon(id) - await driver.navigate().refresh() - await verifyWebExtensionNotInstalled() - }) - - it('installs and uninstalls by unsigned directory', async function () { - await driver.get(Pages.blankPage) - await verifyWebExtensionNotInstalled() - - let id = await driver.installAddon(EXT_UNSIGNED_DIR, true) - - await driver.navigate().refresh() - await verifyWebExtensionWasInstalled() - - await driver.uninstallAddon(id) - await driver.navigate().refresh() - await verifyWebExtensionNotInstalled() - }) - - it('installs and uninstalls by signed directory', async function () { - await driver.get(Pages.blankPage) - await verifyWebExtensionNotInstalled() - - let id = await driver.installAddon(EXT_SIGNED_DIR, true) - - await driver.navigate().refresh() - await verifyWebExtensionWasInstalled() - - await driver.uninstallAddon(id) - await driver.navigate().refresh() - await verifyWebExtensionNotInstalled() - }) - }) - async function verifyUserAgentWasChanged() { let userAgent = await driver.executeScript('return window.navigator.userAgent') assert.strictEqual(userAgent, 'foo;bar') } - async function verifyWebExtensionNotInstalled() { - let found = await driver.findElements({ - id: 'webextensions-selenium-example', - }) - assert.strictEqual(found.length, 0) - } - async function verifyWebExtensionWasInstalled() { let footer = await driver.wait(until.elementLocated(By.id('webextensions-selenium-example')), 5000)