Skip to content

Commit

Permalink
chore: WebKit support (development-only) (#15533)
Browse files Browse the repository at this point in the history
  • Loading branch information
flotwig authored Aug 15, 2022
1 parent 3c172a1 commit 4580330
Show file tree
Hide file tree
Showing 19 changed files with 463 additions and 90 deletions.
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

5 comments on commit 4580330

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4580330 Aug 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.5.1/linux-x64/develop-45803307635a98426a07591cde8ea8ad37cc7140/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4580330 Aug 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.5.1/linux-arm64/develop-45803307635a98426a07591cde8ea8ad37cc7140/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4580330 Aug 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.5.1/darwin-arm64/develop-45803307635a98426a07591cde8ea8ad37cc7140/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4580330 Aug 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.5.1/darwin-x64/develop-45803307635a98426a07591cde8ea8ad37cc7140/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4580330 Aug 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.5.1/win32-x64/develop-45803307635a98426a07591cde8ea8ad37cc7140/cypress.tgz

Please sign in to comment.