From 38338d80bcf98367f079c3d847e2921b698598de Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Fri, 30 Aug 2019 11:10:16 -0400 Subject: [PATCH] add firefox driver integration tests in ci, fix no bypass localhost for proxy in firefox --- circle.yml | 41 +++++-- packages/extension/app/background.coffee | 2 + packages/extension/app/browser-polyfill.js | 116 ------------------- packages/extension/app/manifest.json | 1 - packages/extension/lib/extension.coffee | 6 - packages/extension/package.json | 3 +- packages/server/lib/browsers/chrome.coffee | 19 ++- packages/server/lib/browsers/firefox-util.js | 3 + packages/server/lib/browsers/firefox.coffee | 8 +- packages/server/lib/browsers/index.coffee | 4 +- packages/server/lib/browsers/utils.coffee | 5 +- 11 files changed, 68 insertions(+), 140 deletions(-) delete mode 100644 packages/extension/app/browser-polyfill.js diff --git a/circle.yml b/circle.yml index d1824b323ac9..fb9e6c0d90c1 100644 --- a/circle.yml +++ b/circle.yml @@ -23,7 +23,7 @@ executors: # the Docker image with Cypress dependencies and Chrome browser cy-doc: docker: - - image: cypress/browsers:node8.9.3-npm6.10.1-chrome75 + - image: cypress/browsers:node8.9.3-npm6.10.1-chrome76-ff68 environment: PLATFORM: linux @@ -307,8 +307,8 @@ jobs: path: /tmp/cypress "server-performance-tests": - <<: *defaults - steps: + <<: *defaults + steps: - attach_workspace: at: ~/ - run: @@ -396,8 +396,8 @@ jobs: path: /tmp/cypress "server-e2e-tests-8": - <<: *defaults - steps: + <<: *defaults + steps: - attach_workspace: at: ~/ - run: @@ -406,7 +406,7 @@ jobs: - store_test_results: path: /tmp/cypress - "driver-integration-tests-3x": + "driver-integration-tests-chrome-3x": <<: *defaults parallelism: 3 steps: @@ -430,6 +430,30 @@ jobs: - store_artifacts: path: /tmp/artifacts + "driver-integration-tests-firefox-3x": + <<: *defaults + parallelism: 3 + steps: + - attach_workspace: + at: ~/ + - run: + command: npm start + background: true + working_directory: packages/driver + - run: + command: $(npm bin)/wait-on http://localhost:3500 + working_directory: packages/driver + - run: + command: | + CYPRESS_KONFIG_ENV=production \ + CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \ + npm run cypress:run -- --record --parallel --group 3x-driver-firefox --browser firefox + working_directory: packages/driver + - store_test_results: + path: /tmp/cypress + - store_artifacts: + path: /tmp/artifacts + "desktop-gui-integration-tests-2x": <<: *defaults parallelism: 2 @@ -796,7 +820,10 @@ linux-workflow: &linux-workflow - server-e2e-tests-8: requires: - build - - driver-integration-tests-3x: + - driver-integration-tests-chrome-3x: + requires: + - build + - driver-integration-tests-firefox-3x: requires: - build - desktop-gui-integration-tests-2x: diff --git a/packages/extension/app/background.coffee b/packages/extension/app/background.coffee index c04a28d941de..6dffca6845fd 100644 --- a/packages/extension/app/background.coffee +++ b/packages/extension/app/background.coffee @@ -1,3 +1,4 @@ +browser = require('webextension-polyfill') map = require("lodash/map") pick = require("lodash/pick") once = require("lodash/once") @@ -6,6 +7,7 @@ client = require("./client") httpRe = /^http/ +debugger firstOrNull = (cookies) -> ## normalize into null when empty array cookies[0] ? null diff --git a/packages/extension/app/browser-polyfill.js b/packages/extension/app/browser-polyfill.js deleted file mode 100644 index 18ad77ff1421..000000000000 --- a/packages/extension/app/browser-polyfill.js +++ /dev/null @@ -1,116 +0,0 @@ -(function (a, b) { - if ('function' === typeof define && define.amd) { - define('webextension-polyfill', ['module'], b) - } else if ('undefined' !== typeof exports) { - b(module) - } else { - let c = { exports: {} } - - b(c), a.browser = c.exports - } -})(this, function (a) { - 'use strict'; if ('undefined' === typeof browser) { - a.exports = (() => { - const c = { alarms: { clear: { minArgs: 0, maxArgs: 1 }, clearAll: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 0, maxArgs: 1 }, getAll: { minArgs: 0, maxArgs: 0 } }, bookmarks: { create: { minArgs: 1, maxArgs: 1 }, export: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 1, maxArgs: 1 }, getChildren: { minArgs: 1, maxArgs: 1 }, getRecent: { minArgs: 1, maxArgs: 1 }, getTree: { minArgs: 0, maxArgs: 0 }, getSubTree: { minArgs: 1, maxArgs: 1 }, import: { minArgs: 0, maxArgs: 0 }, move: { minArgs: 2, maxArgs: 2 }, remove: { minArgs: 1, maxArgs: 1 }, removeTree: { minArgs: 1, maxArgs: 1 }, search: { minArgs: 1, maxArgs: 1 }, update: { minArgs: 2, maxArgs: 2 } }, browserAction: { getBadgeBackgroundColor: { minArgs: 1, maxArgs: 1 }, getBadgeText: { minArgs: 1, maxArgs: 1 }, getPopup: { minArgs: 1, maxArgs: 1 }, getTitle: { minArgs: 1, maxArgs: 1 }, setIcon: { minArgs: 1, maxArgs: 1 } }, commands: { getAll: { minArgs: 0, maxArgs: 0 } }, contextMenus: { update: { minArgs: 2, maxArgs: 2 }, remove: { minArgs: 1, maxArgs: 1 }, removeAll: { minArgs: 0, maxArgs: 0 } }, cookies: { get: { minArgs: 1, maxArgs: 1 }, getAll: { minArgs: 1, maxArgs: 1 }, getAllCookieStores: { minArgs: 0, maxArgs: 0 }, remove: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } }, devtools: { inspectedWindow: { eval: { minArgs: 1, maxArgs: 2 } }, panels: { create: { minArgs: 3, maxArgs: 3, singleCallbackArg: !0 } } }, downloads: { download: { minArgs: 1, maxArgs: 1 }, cancel: { minArgs: 1, maxArgs: 1 }, erase: { minArgs: 1, maxArgs: 1 }, getFileIcon: { minArgs: 1, maxArgs: 2 }, open: { minArgs: 1, maxArgs: 1 }, pause: { minArgs: 1, maxArgs: 1 }, removeFile: { minArgs: 1, maxArgs: 1 }, resume: { minArgs: 1, maxArgs: 1 }, search: { minArgs: 1, maxArgs: 1 }, show: { minArgs: 1, maxArgs: 1 } }, extension: { isAllowedFileSchemeAccess: { minArgs: 0, maxArgs: 0 }, isAllowedIncognitoAccess: { minArgs: 0, maxArgs: 0 } }, history: { addUrl: { minArgs: 1, maxArgs: 1 }, getVisits: { minArgs: 1, maxArgs: 1 }, deleteAll: { minArgs: 0, maxArgs: 0 }, deleteRange: { minArgs: 1, maxArgs: 1 }, deleteUrl: { minArgs: 1, maxArgs: 1 }, search: { minArgs: 1, maxArgs: 1 } }, i18n: { detectLanguage: { minArgs: 1, maxArgs: 1 }, getAcceptLanguages: { minArgs: 0, maxArgs: 0 } }, idle: { queryState: { minArgs: 1, maxArgs: 1 } }, management: { get: { minArgs: 1, maxArgs: 1 }, getAll: { minArgs: 0, maxArgs: 0 }, getSelf: { minArgs: 0, maxArgs: 0 }, uninstallSelf: { minArgs: 0, maxArgs: 1 } }, notifications: { clear: { minArgs: 1, maxArgs: 1 }, create: { minArgs: 1, maxArgs: 2 }, getAll: { minArgs: 0, maxArgs: 0 }, getPermissionLevel: { minArgs: 0, maxArgs: 0 }, update: { minArgs: 2, maxArgs: 2 } }, pageAction: { getPopup: { minArgs: 1, maxArgs: 1 }, getTitle: { minArgs: 1, maxArgs: 1 }, hide: { minArgs: 0, maxArgs: 0 }, setIcon: { minArgs: 1, maxArgs: 1 }, show: { minArgs: 0, maxArgs: 0 } }, runtime: { getBackgroundPage: { minArgs: 0, maxArgs: 0 }, getBrowserInfo: { minArgs: 0, maxArgs: 0 }, getPlatformInfo: { minArgs: 0, maxArgs: 0 }, openOptionsPage: { minArgs: 0, maxArgs: 0 }, requestUpdateCheck: { minArgs: 0, maxArgs: 0 }, sendMessage: { minArgs: 1, maxArgs: 3 }, sendNativeMessage: { minArgs: 2, maxArgs: 2 }, setUninstallURL: { minArgs: 1, maxArgs: 1 } }, storage: { local: { clear: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 0, maxArgs: 1 }, getBytesInUse: { minArgs: 0, maxArgs: 1 }, remove: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } }, managed: { get: { minArgs: 0, maxArgs: 1 }, getBytesInUse: { minArgs: 0, maxArgs: 1 } }, sync: { clear: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 0, maxArgs: 1 }, getBytesInUse: { minArgs: 0, maxArgs: 1 }, remove: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } } }, tabs: { create: { minArgs: 1, maxArgs: 1 }, captureVisibleTab: { minArgs: 0, maxArgs: 2 }, detectLanguage: { minArgs: 0, maxArgs: 1 }, duplicate: { minArgs: 1, maxArgs: 1 }, executeScript: { minArgs: 1, maxArgs: 2 }, get: { minArgs: 1, maxArgs: 1 }, getCurrent: { minArgs: 0, maxArgs: 0 }, getZoom: { minArgs: 0, maxArgs: 1 }, getZoomSettings: { minArgs: 0, maxArgs: 1 }, highlight: { minArgs: 1, maxArgs: 1 }, insertCSS: { minArgs: 1, maxArgs: 2 }, move: { minArgs: 2, maxArgs: 2 }, reload: { minArgs: 0, maxArgs: 2 }, remove: { minArgs: 1, maxArgs: 1 }, query: { minArgs: 1, maxArgs: 1 }, removeCSS: { minArgs: 1, maxArgs: 2 }, sendMessage: { minArgs: 2, maxArgs: 3 }, setZoom: { minArgs: 1, maxArgs: 2 }, setZoomSettings: { minArgs: 1, maxArgs: 2 }, update: { minArgs: 1, maxArgs: 2 } }, webNavigation: { getAllFrames: { minArgs: 1, maxArgs: 1 }, getFrame: { minArgs: 1, maxArgs: 1 } }, webRequest: { handlerBehaviorChanged: { minArgs: 0, maxArgs: 0 } }, windows: { create: { minArgs: 0, maxArgs: 1 }, get: { minArgs: 1, maxArgs: 2 }, getAll: { minArgs: 0, maxArgs: 1 }, getCurrent: { minArgs: 0, maxArgs: 1 }, getLastFocused: { minArgs: 0, maxArgs: 1 }, remove: { minArgs: 1, maxArgs: 1 }, update: { minArgs: 2, maxArgs: 2 } } } - - if (0 === Object.keys(c).length) throw new Error('api-metadata.json has not been included in browser-polyfill') - - class d extends WeakMap { - constructor (o, p = void 0) { - super(p), this.createItem = o - }get (o) { - return this.has(o) || this.set(o, this.createItem(o)), super.get(o) - } - } const e = (o) => { - return o && 'object' === typeof o && 'function' === typeof o.then - }; const f = (o, p) => { - return (...q) => { - chrome.runtime.lastError ? o.reject(chrome.runtime.lastError) : p.singleCallbackArg || 1 === q.length ? o.resolve(q[0]) : o.resolve(q) - } - }; const g = (o, p) => { - const q = (r) => 1 == r ? 'argument' : 'arguments' - - return function (s, ...t) { - if (t.length < p.minArgs) throw new Error(`Expected at least ${p.minArgs} ${q(p.minArgs)} for ${o}(), got ${t.length}`) - - if (t.length > p.maxArgs) throw new Error(`Expected at most ${p.maxArgs} ${q(p.maxArgs)} for ${o}(), got ${t.length}`) - - return new Promise((u, v) => { - s[o](...t, f({ resolve: u, reject: v }, p)) - }) - } - }; const h = (o, p, q) => { - return new Proxy(p, { apply (r, s, t) { - return q.call(s, o, ...t) - } }) - }; let i = Function.call.bind(Object.prototype.hasOwnProperty); const j = (o, p = {}, q = {}) => { - let r = Object.create(null); let s = { has (t, u) { - return u in t || u in r - }, get (t, u) { - if (u in r) return r[u] - - if (u in t) { - let w = t[u] - - if ('function' === typeof w) { - if ('function' === typeof p[u]) { - w = h(t, t[u], p[u]) - } else if (i(q, u)) { - let x = g(u, q[u]) - - w = h(t, t[u], x) - } else { - w = w.bind(t) - } - } else if ('object' === typeof w && null !== w && (i(p, u) || i(q, u))) { - w = j(w, p[u], q[u]) - } else { - return Object.defineProperty(r, u, { configurable: !0, enumerable: !0, get () { - return t[u] - }, set (x) { - t[u] = x - } }), w - } - - return r[u] = w, w - } - }, set (t, u, v) { - return u in r ? r[u] = v : t[u] = v, !0 - }, defineProperty (t, u, v) { - return Reflect.defineProperty(r, u, v) - }, deleteProperty (t, u) { - return Reflect.deleteProperty(r, u) - } } - - return new Proxy(o, s) - }; const l = new d((o) => { - return 'function' === typeof o ? function (q, r, s) { - let t = o(q, r) - - return e(t) ? (t.then(s, (u) => { - console.error(u), s(u) - }), !0) : void (void 0 !== t && s(t)) - } : o - }); const m = { runtime: { onMessage: ((o) => { - return { addListener (p, q, ...r) { - p.addListener(o.get(q), ...r) - }, hasListener (p, q) { - return p.hasListener(o.get(q)) - }, removeListener (p, q) { - p.removeListener(o.get(q)) - } } - })(l) } }; const n = Object.assign({}, chrome) - - return j(n, m, c) - })() - } else { - a.exports = browser - } -}) -//# sourceMappingURL=browser-polyfill.min.js.map - -// webextension-polyfill v.0.2.1 (https://github.com/mozilla/webextension-polyfill) - -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/packages/extension/app/manifest.json b/packages/extension/app/manifest.json index f9e003318a31..a2c8268b7f05 100644 --- a/packages/extension/app/manifest.json +++ b/packages/extension/app/manifest.json @@ -29,7 +29,6 @@ }, "background": { "scripts": [ - "browser-polyfill.js", "background.js" ] }, diff --git a/packages/extension/lib/extension.coffee b/packages/extension/lib/extension.coffee index b0152b2d79f7..747239db38ce 100644 --- a/packages/extension/lib/extension.coffee +++ b/packages/extension/lib/extension.coffee @@ -1,7 +1,6 @@ fs = require("fs") path = require("path") Promise = require("bluebird") -background = require("../app/background") fs = Promise.promisifyAll(fs) @@ -26,9 +25,4 @@ module.exports = { .replace("CHANGE_ME_HOST", host) .replace("CHANGE_ME_PATH", path) - getCookieUrl: background.getUrl - - connect: background.connect - - app: background } diff --git a/packages/extension/package.json b/packages/extension/package.json index 690a048f6b68..a734bc2bdf98 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -38,7 +38,8 @@ "run-sequence": "1.2.2", "sinon": "7.3.2", "sinon-chai": "3.3.0", - "vinyl-source-stream": "2.0.0" + "vinyl-source-stream": "2.0.0", + "webextension-polyfill": "0.4.0" }, "files": [ "app", diff --git a/packages/server/lib/browsers/chrome.coffee b/packages/server/lib/browsers/chrome.coffee index aff17a85a68d..a3336eba4b93 100644 --- a/packages/server/lib/browsers/chrome.coffee +++ b/packages/server/lib/browsers/chrome.coffee @@ -130,6 +130,20 @@ module.exports = { _removeRootExtension + _writeExtension: (browser, isTextTerminal, proxyUrl, socketIoRoute) -> + ## get the string bytes for the final extension file + extension.setHostAndPath(proxyUrl, socketIoRoute) + .then (str) -> + extensionDest = utils.getExtensionDir(browser, isTextTerminal) + extensionBg = path.join(extensionDest, "background.js") + + ## copy the extension src to the extension dist + utils.copyExtension(pathToExtension, extensionDest) + .then -> + ## and overwrite background.js with the final string bytes + fs.writeFileAsync(extensionBg, str) + .return(extensionDest) + _getArgs: (options = {}) -> _.defaults(options, { browser: {} @@ -160,7 +174,7 @@ module.exports = { ## https://github.com/cypress-io/cypress/issues/2223 { majorVersion } = options.browser if majorVersion in CHROME_VERSIONS_WITH_BUGGY_ROOT_LAYER_SCROLLING - args.push("--disable-blink-features=RootLayerScrolling") + args.push("--disable-blink-features=RootLayerScrolling") ## https://chromium.googlesource.com/chromium/src/+/da790f920bbc169a6805a4fb83b4c2ab09532d91 ## https://github.com/cypress-io/cypress/issues/1872 @@ -187,7 +201,8 @@ module.exports = { ]) .spread (cacheDir, args) => Promise.all([ - utils.writeExtension( + # utils.writeExtension( + @_writeExtension( browser, isTextTerminal, options.proxyUrl, diff --git a/packages/server/lib/browsers/firefox-util.js b/packages/server/lib/browsers/firefox-util.js index f5c6910410b8..47e2de85a2a9 100644 --- a/packages/server/lib/browsers/firefox-util.js +++ b/packages/server/lib/browsers/firefox-util.js @@ -3,6 +3,7 @@ const Marionette = require('marionette-client') const Exception = require('marionette-client/lib/marionette/error') const Command = require('marionette-client/lib/marionette/message.js').Command const Promise = require('bluebird') +const debug = require('debug')('cypress:server:browsers') const promisify = (fn) => { return (...args) => { @@ -31,6 +32,8 @@ module.exports = { send, setup (extensions, url) { + debug('firefox: navigating page with webdriver') + return connect() .then(() => { return send({ diff --git a/packages/server/lib/browsers/firefox.coffee b/packages/server/lib/browsers/firefox.coffee index a5d0c8306aa2..0c589e855f2c 100644 --- a/packages/server/lib/browsers/firefox.coffee +++ b/packages/server/lib/browsers/firefox.coffee @@ -102,12 +102,14 @@ module.exports = { preferences = _.extend({}, defaultPreferences) extensions = [] + debug('firefox open', options) if ps = options.proxyServer {hostname, port, protocol} = urlUtil.parse(ps) port ?= if protocol is "https:" then 443 else 80 port = parseFloat(port) _.extend(preferences, { + "network.proxy.allow_hijacking_localhost": true "network.proxy.http": hostname "network.proxy.ssl": hostname "network.proxy.http_port": port @@ -134,7 +136,7 @@ module.exports = { .then -> Promise.all([ utils.ensureCleanCache(browserName) - utils.writeExtension(options.browser, options.isTextTerminal, options.proxyUrl, options.socketIoRoute) + utils.writeExtension(options.browser, options.visTextTerminal, options.proxyUrl, options.socketIoRoute) utils.ensureCleanCache(browserName) ]) .spread (cacheDir, extensionDest, profileDir) -> @@ -163,8 +165,8 @@ module.exports = { utils.launch(browserName, null, args) .then (browserInstance) -> firefoxUtil.setup(extensions, url) - # .then -> - # return browserInstance + .then -> + return browserInstance .catch (err) -> debug("launch error:", err.stack) throw err diff --git a/packages/server/lib/browsers/index.coffee b/packages/server/lib/browsers/index.coffee index 524b27138946..f833494c5cff 100644 --- a/packages/server/lib/browsers/index.coffee +++ b/packages/server/lib/browsers/index.coffee @@ -112,9 +112,9 @@ module.exports = { debug("browser opened") ## TODO: bind to process.exit here ## or move this functionality into cypress-core-launder - + instance = i - + debug('instance', instance) ## TODO: normalizing opening and closing / exiting ## so that there is a default for each browser but ## enable the browser to configure the interface diff --git a/packages/server/lib/browsers/utils.coffee b/packages/server/lib/browsers/utils.coffee index af653147ab1e..81b7ba524047 100644 --- a/packages/server/lib/browsers/utils.coffee +++ b/packages/server/lib/browsers/utils.coffee @@ -5,7 +5,7 @@ fs = require("../util/fs") extension = require("@packages/extension") appData = require("../util/app_data") profileCleaner = require("../util/profile_cleaner") - +debug = require('debug')('cypress:server:browsers') PATH_TO_BROWSERS = appData.path("browsers") getBrowserPath = (browser) -> @@ -88,7 +88,7 @@ module.exports = { launch: launcher.launch writeExtension: (browser, isTextTerminal, proxyUrl, socketIoRoute) -> - require('debug')('cypress:server:browsers')(browser, isTextTerminal, proxyUrl) + debug('writing extension') # debug('writing extension to chrome browser') ## get the string bytes for the final extension file extension.setHostAndPath(proxyUrl, socketIoRoute) @@ -99,6 +99,7 @@ module.exports = { ## copy the extension src to the extension dist copyExtension(pathToExtension, extensionDest) .then -> + debug('copied extension') ## and overwrite background.js with the final string bytes fs.writeFileAsync(extensionBg, str) .return(extensionDest)