Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: WebKit support (development-only) #15533

Merged
merged 28 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de8e6c3
detect playwright-webkit browser
flotwig Mar 16, 2021
d0d1736
fix: use stdio for CDP instead of TCP (#14348)
flotwig Jan 19, 2021
818c7de
wip: begin launchin webkit
flotwig Mar 16, 2021
a7279e5
Merge remote-tracking branch 'origin/develop' into cy-webkit
flotwig Aug 2, 2022
45b5286
run mode works w webkit in 10.0
flotwig Aug 4, 2022
ecd6f0f
reset previous cdp changes
flotwig Aug 4, 2022
505c0dd
run driver webkit tests
flotwig Aug 4, 2022
35b4039
always detect webkit in non-prod
flotwig Aug 5, 2022
ceba06f
fix version detection
flotwig Aug 5, 2022
68ebaca
actually run new job
flotwig Aug 4, 2022
a5a0fc6
Merge remote-tracking branch 'origin/develop' into cy-webkit
flotwig Aug 5, 2022
f974e99
cleanup
flotwig Aug 5, 2022
25b8fc9
fix run
flotwig Aug 5, 2022
411a2f1
try caching pw binary
flotwig Aug 5, 2022
83c58d5
npx install pw binary
flotwig Aug 5, 2022
def14cc
install-deps
flotwig Aug 5, 2022
e85025c
add experimentalSessionAndOrigin wk tests
flotwig Aug 5, 2022
53db500
wk experimentalSessionAndOrigin tests
flotwig Aug 5, 2022
12b3cab
browser icon
flotwig Aug 8, 2022
37de56a
fix some tests
flotwig Aug 8, 2022
32ed232
reset browsers.ts change
flotwig Aug 8, 2022
3acc19b
fix more tests
flotwig Aug 8, 2022
0681f0a
fix even more tests, skip driver CI for now
flotwig Aug 8, 2022
67fdd2d
comma
flotwig Aug 8, 2022
58b2d39
fix server-unit-test
flotwig Aug 8, 2022
406d8ec
fix websockets_spec
flotwig Aug 8, 2022
9ac53fd
Suggestions from code review
flotwig Aug 15, 2022
ac70fc8
Merge branch 'develop' into cy-webkit
flotwig Aug 15, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,18 @@ commands:
channel: <<parameters.install-chrome-channel>>
version: $(node ./scripts/get-browser-version.js chrome:<<parameters.install-chrome-channel>>)
- run:
name: Run driver tests in Cypress
environment:
CYPRESS_KONFIG_ENV: production
command: |
echo Current working directory is $PWD
echo Total containers $CIRCLE_NODE_TOTAL

if [[ "<<parameters.browser>>" = "webkit" ]]; then
npx playwright install webkit
npx playwright install-deps webkit
fi

if [[ -v MAIN_RECORD_KEY ]]; then
# internal PR
if <<parameters.experimentalSessionAndOrigin>>; then
Expand Down Expand Up @@ -1565,6 +1571,13 @@ jobs:
- run-driver-integration-tests:
browser: electron

driver-integration-tests-webkit:
<<: *defaults
parallelism: 5
steps:
- run-driver-integration-tests:
browser: webkit

driver-integration-tests-chrome-experimentalSessionAndOrigin:
<<: *defaults
resource_class: medium
Expand Down Expand Up @@ -1603,6 +1616,15 @@ jobs:
browser: electron
experimentalSessionAndOrigin: true

driver-integration-tests-webkit-experimentalSessionAndOrigin:
<<: *defaults
resource_class: medium
parallelism: 5
steps:
- run-driver-integration-tests:
browser: webkit
experimentalSessionAndOrigin: true

run-reporter-component-tests-chrome:
<<: *defaults
parameters:
Expand Down Expand Up @@ -2359,6 +2381,11 @@ linux-x64-workflow: &linux-x64-workflow
context: test-runner:cypress-record-key
requires:
- build
# TODO: Fix keyboard tests to fix the majority of these tests before re-enabling
# - driver-integration-tests-webkit:
# context: test-runner:cypress-record-key
# requires:
# - build
- driver-integration-tests-chrome-experimentalSessionAndOrigin:
context: test-runner:cypress-record-key
requires:
Expand All @@ -2375,6 +2402,11 @@ linux-x64-workflow: &linux-x64-workflow
context: test-runner:cypress-record-key
requires:
- build
# TODO: Implement WebKit network automation to fix the majority of these tests before re-enabling
# - driver-integration-tests-webkit-experimentalSessionAndOrigin:
# context: test-runner:cypress-record-key
# requires:
# - build
- run-frontend-shared-component-tests-chrome:
context: [test-runner:cypress-record-key, test-runner:launchpad-tests, test-runner:percy]
percy: true
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
"mock-fs": "5.1.1",
"p-defer": "^3.0.0",
"patch-package": "6.4.7",
"playwright-webkit": "1.24.2",
"pluralize": "8.0.0",
"postinstall-postinstall": "2.0.0",
"print-arch": "1.0.0",
Expand Down
6 changes: 6 additions & 0 deletions packages/app/src/runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export function createWebsocket (socketIoRoute: string) {

const ws = client(socketConfig)

ws.on('connect_error', () => {
// fall back to polling if websocket fails to connect (webkit)
// https://github.com/socketio/socket.io/discussions/3998#discussioncomment-972316
ws.io.opts.transports = ['polling', 'websocket']
})

ws.on('connect', () => {
ws.emit('runner:connected')
})
Expand Down
2 changes: 1 addition & 1 deletion packages/config/__snapshots__/validation.spec.ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ exports['config/src/validation .isValidBrowser passes valid browsers and forms e
"displayName": "Bad family browser",
"family": "unknown family"
},
"type": "either chromium or firefox"
"type": "either chromium, firefox or webkit"
}
}
]
Expand Down
8 changes: 3 additions & 5 deletions packages/config/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as _ from 'lodash'
import * as is from 'check-more-types'
import { commaListsOr } from 'common-tags'
import Debug from 'debug'
import { BROWSER_FAMILY } from '@packages/types'

const debug = Debug('cypress:server:validation')

Expand Down Expand Up @@ -49,11 +50,8 @@ export const isValidBrowser = (browser: any): ErrResult | true => {
return errMsg('name', browser, 'a non-empty string')
}

// TODO: this is duplicated with browsers/index
const knownBrowserFamilies = ['chromium', 'firefox']

if (!is.oneOf(knownBrowserFamilies)(browser.family)) {
return errMsg('family', browser, commaListsOr`either ${knownBrowserFamilies}`)
if (!is.oneOf(BROWSER_FAMILY)(browser.family)) {
return errMsg('family', browser, commaListsOr`either ${BROWSER_FAMILY}`)
}

if (!is.unemptyString(browser.displayName)) {
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend-shared/src/assets/browserLogos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import edgeCanaryIcon from '../../../../node_modules/browser-logos/src/edge-cana
import edgeDevIcon from '../../../../node_modules/browser-logos/src/edge-dev/edge-dev.png'
import firefoxNightlyIcon from '../../../../node_modules/browser-logos/src/firefox-nightly/firefox-nightly.svg?url'
import firefoxDeveloperEditionIcon from '../../../../node_modules/browser-logos/src/firefox-developer-edition/firefox-developer-edition.svg?url'
import webKitIcon from '../../../../node_modules/browser-logos/src/webkit/webkit.svg?url'
import genericBrowserLogo from '@packages/frontend-shared/src/assets/logos/generic-browser.svg?url'

export const allBrowsersIcons = {
Expand All @@ -25,5 +26,6 @@ export const allBrowsersIcons = {
'Edge Canary': edgeCanaryIcon,
'Edge Beta': edgeBetaIcon,
'Edge Dev': edgeDevIcon,
'WebKit': webKitIcon,
'generic': genericBrowserLogo,
}
1 change: 1 addition & 0 deletions packages/graphql/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Browser implements Node {
enum BrowserFamily {
chromium
firefox
webkit
}

enum BrowserStatus {
Expand Down
8 changes: 6 additions & 2 deletions packages/server/lib/browsers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const check = require('check-more-types')
const { exec } = require('child_process')
const util = require('util')
const os = require('os')
const { BROWSER_FAMILY } = require('@packages/types')

// returns true if the passed string is a known browser family name
const isBrowserFamily = check.oneOf(['chromium', 'firefox'])
const isBrowserFamily = check.oneOf(BROWSER_FAMILY)

let instance = null

Expand Down Expand Up @@ -79,6 +79,10 @@ const getBrowserLauncher = function (browser) {
if (browser.family === 'firefox') {
return require('./firefox')
}

if (browser.family === 'webkit') {
return require('./webkit')
}
}

process.once('exit', () => kill(true, true))
Expand Down
164 changes: 105 additions & 59 deletions packages/server/lib/browsers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import type { FoundBrowser } from '@packages/types'
import * as errors from '../errors'
import * as plugins from '../plugins'
import { getError } from '@packages/errors'
import * as launcher from '@packages/launcher'

const path = require('path')
const debug = require('debug')('cypress:server:browsers:utils')
const getPort = require('get-port')
const launcher = require('@packages/launcher')
const { fs } = require('../util/fs')
const extension = require('@packages/extension')
const appData = require('../util/app_data')
Expand Down Expand Up @@ -182,44 +182,93 @@ function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, opti
return launchOptions
}

const getBrowsers = () => {
debug('getBrowsers')
const wkBrowserVersionRe = /BROWSER_VERSION = \'(?<version>[^']+)\'/gm

return launcher.detect()
.then((browsers: FoundBrowser[] = []) => {
let majorVersion
const getWebKitBrowserVersion = async () => {
try {
// this seems to be the only way to accurately capture the WebKit version - it's not exported, and invoking the webkit binary with `--version` does not give the correct result
// after launching the browser, this is available at browser.version(), but we don't have a browser instance til later
const pwCorePath = path.dirname(require.resolve('playwright-core', { paths: [process.cwd()] }))
const wkBrowserPath = path.join(pwCorePath, 'lib', 'server', 'webkit', 'wkBrowser.js')
const wkBrowserContents = await fs.readFile(wkBrowserPath)
const result = wkBrowserVersionRe.exec(wkBrowserContents)

debug('found browsers %o', { browsers })
if (!result || !result.groups!.version) return '0'

if (!process.versions.electron) {
debug('not in electron, skipping adding electron browser')
return result.groups!.version
} catch (err) {
debug('Error detecting WebKit browser version %o', err)

return browsers
}

// @ts-ignore
const version = process.versions.chrome || ''
return '0'
}
}

if (version) {
majorVersion = getMajorVersion(version)
}
const getWebKitBrowser = async () => {
try {
const modulePath = require.resolve('playwright-webkit', { paths: [process.cwd()] })
const mod = require(modulePath) as typeof import('playwright-webkit')
const version = await getWebKitBrowserVersion()

const electronBrowser: FoundBrowser = {
name: 'electron',
const browser: FoundBrowser = {
name: 'webkit',
channel: 'stable',
family: 'chromium',
displayName: 'Electron',
family: 'webkit',
displayName: 'WebKit',
version,
path: '',
majorVersion,
info: 'Electron is the default browser that comes with Cypress. This is the default browser that runs in headless mode. Selecting this browser is useful when debugging. The version number indicates the underlying Chromium version that Electron uses.',
path: mod.webkit.executablePath(),
majorVersion: version.split('.')[0],
warning: 'WebKit support is not currently available in production.',
}

// the internal version of Electron, which won't be detected by `launcher`
debug('adding Electron browser %o', electronBrowser)
return browser
} catch (err) {
debug('WebKit is enabled, but there was an error constructing the WebKit browser: %o', { err })

return browsers.concat(electronBrowser)
})
return
}
}

const getBrowsers = async () => {
debug('getBrowsers')

const browsers = await launcher.detect()
let majorVersion

debug('found browsers %o', { browsers })

if (!process.versions.electron) {
debug('not in electron, skipping adding electron browser')

return browsers
}

// @ts-ignore
const version = process.versions.chrome || ''

if (version) {
majorVersion = getMajorVersion(version)
}

const electronBrowser: FoundBrowser = {
name: 'electron',
channel: 'stable',
family: 'chromium',
displayName: 'Electron',
version,
path: '',
majorVersion,
info: 'Electron is the default browser that comes with Cypress. This is the default browser that runs in headless mode. Selecting this browser is useful when debugging. The version number indicates the underlying Chromium version that Electron uses.',
}

browsers.push(electronBrowser)

if (process.env.CYPRESS_INTERNAL_ENV !== 'production') {
const wkBrowser = await getWebKitBrowser()

if (wkBrowser) browsers.push(wkBrowser)
}

return browsers
}

const isValidPathToBrowser = (str) => {
Expand Down Expand Up @@ -247,47 +296,44 @@ const parseBrowserOption = (opt) => {
function ensureAndGetByNameOrPath(nameOrPath: string, returnAll: false, browsers: FoundBrowser[]): Bluebird<FoundBrowser>
function ensureAndGetByNameOrPath(nameOrPath: string, returnAll: true, browsers: FoundBrowser[]): Bluebird<FoundBrowser[]>

function ensureAndGetByNameOrPath (nameOrPath: string, returnAll = false, browsers: FoundBrowser[] = []) {
const findBrowsers = browsers.length ? Bluebird.resolve(browsers) : getBrowsers()
async function ensureAndGetByNameOrPath (nameOrPath: string, returnAll = false, prevKnownBrowsers: FoundBrowser[] = []) {
const browsers = prevKnownBrowsers.length ? prevKnownBrowsers : (await getBrowsers())

const filter = parseBrowserOption(nameOrPath)

return findBrowsers
.then((browsers: FoundBrowser[] = []) => {
const filter = parseBrowserOption(nameOrPath)
debug('searching for browser %o', { nameOrPath, filter, knownBrowsers: browsers })

debug('searching for browser %o', { nameOrPath, filter, knownBrowsers: browsers })
// try to find the browser by name with the highest version property
const sortedBrowsers = _.sortBy(browsers, ['version'])

// try to find the browser by name with the highest version property
const sortedBrowsers = _.sortBy(browsers, ['version'])
const browser = _.findLast(sortedBrowsers, filter)

const browser = _.findLast(sortedBrowsers, filter)
if (browser) {
// short circuit if found
if (returnAll) {
return browsers
}

if (browser) {
// short circuit if found
return browser
}

// did the user give a bad name, or is this actually a path?
if (isValidPathToBrowser(nameOrPath)) {
// looks like a path - try to resolve it to a FoundBrowser
return launcher.detectByPath(nameOrPath)
.then((browser) => {
if (returnAll) {
return browsers
return [browser].concat(browsers)
}

return browser
}

// did the user give a bad name, or is this actually a path?
if (isValidPathToBrowser(nameOrPath)) {
// looks like a path - try to resolve it to a FoundBrowser
return launcher.detectByPath(nameOrPath)
.then((browser) => {
if (returnAll) {
return [browser].concat(browsers)
}

return browser
}).catch((err) => {
errors.throwErr('BROWSER_NOT_FOUND_BY_PATH', nameOrPath, err.message)
})
}
}).catch((err) => {
errors.throwErr('BROWSER_NOT_FOUND_BY_PATH', nameOrPath, err.message)
})
}

// not a path, not found by name
throwBrowserNotFound(nameOrPath, browsers)
})
// not a path, not found by name
throwBrowserNotFound(nameOrPath, browsers)
}

const formatBrowsersToOptions = (browsers) => {
Expand Down
Loading