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): add video recording #23579

Merged
merged 32 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
297e41b
factor console logging out of run.ts
flotwig Aug 25, 2022
173de9f
fix print-run
flotwig Aug 25, 2022
e493600
minimize diff
flotwig Aug 25, 2022
beb3f2d
chore(server): convert browsers/index to typescript
flotwig Aug 25, 2022
4957a83
fix tests
flotwig Aug 25, 2022
c255548
update stubbed tests
flotwig Aug 25, 2022
b01e8c0
convert electron.js to .ts
flotwig Aug 25, 2022
ce15192
Suggestions from code review
flotwig Aug 26, 2022
8bd49f3
Clean up new type errors
flotwig Aug 26, 2022
8e8f62a
electron.connectToExisting can be sync
flotwig Aug 26, 2022
501895a
more type errors for the type god
flotwig Aug 26, 2022
5e74399
Suggestions from code review
flotwig Aug 26, 2022
d25b9e0
refactor: move more of video capture into browser automations
flotwig Aug 27, 2022
9b8cfbe
unit tests
flotwig Aug 29, 2022
a7f6d4b
Merge remote-tracking branch 'origin/develop' into refactor-defaultBr…
flotwig Aug 29, 2022
e3b1979
refactor: move videoCapture to browsers
flotwig Aug 30, 2022
c3f6915
fix snapshots
flotwig Aug 30, 2022
ce0a27f
fix multi-spec videos?
flotwig Aug 30, 2022
e41dd70
webkit video recording works!
flotwig Aug 30, 2022
645a8d7
webkit system tests
flotwig Aug 30, 2022
059748b
skip system-tests that won't be fixed in this PR
flotwig Aug 31, 2022
84554e4
fix single-tab mode
flotwig Aug 31, 2022
2b9f33f
cleanup/api renames
flotwig Aug 31, 2022
115a07c
fix more tests
flotwig Aug 31, 2022
4ad2279
minimize diff, fix ff
flotwig Aug 31, 2022
a449c8f
fix unit tests
flotwig Aug 31, 2022
2468f9f
fix tests
flotwig Aug 31, 2022
d38ae27
cleanup
flotwig Aug 31, 2022
47adf21
Merge remote-tracking branch 'origin/develop' into webkit-video
flotwig Sep 1, 2022
22ddb94
clean up wantsWrite logic
flotwig Sep 2, 2022
45e9b8f
Clean up setVideoController/newFfmpegVideoController naming
flotwig Sep 6, 2022
edff645
Merge branch 'develop' into webkit-video
flotwig Sep 6, 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
34 changes: 29 additions & 5 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ commands:
mv ~/cypress/system-tests/node_modules /tmp/node_modules_cache/system-tests_node_modules
mv ~/cypress/globbed_node_modules /tmp/node_modules_cache/globbed_node_modules

install-webkit-deps:
steps:
- run:
name: Install WebKit dependencies
command: |
npx playwright install webkit
npx playwright install-deps webkit

build-and-persist:
description: Save entire folder as artifact for other jobs to run without reinstalling
steps:
Expand Down Expand Up @@ -462,6 +470,11 @@ commands:
- install-chrome:
channel: <<parameters.install-chrome-channel>>
version: $(node ./scripts/get-browser-version.js chrome:<<parameters.install-chrome-channel>>)
- when:
condition:
equal: [ webkit, << parameters.browser >> ]
steps:
- install-webkit-deps
- run:
name: Run driver tests in Cypress
environment:
Expand All @@ -470,11 +483,6 @@ commands:
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 @@ -610,6 +618,11 @@ commands:
steps:
- restore_cached_workspace
- restore_cached_system_tests_deps
- when:
condition:
equal: [ webkit, << parameters.browser >> ]
steps:
- install-webkit-deps
- run:
name: Run system tests
command: |
Expand Down Expand Up @@ -1448,6 +1461,13 @@ jobs:
- run-system-tests:
browser: firefox

system-tests-webkit:
<<: *defaults
parallelism: 8
steps:
- run-system-tests:
browser: webkit

system-tests-non-root:
<<: *defaults
steps:
Expand Down Expand Up @@ -2363,6 +2383,10 @@ linux-x64-workflow: &linux-x64-workflow
context: test-runner:performance-tracking
requires:
- system-tests-node-modules-install
- system-tests-webkit:
context: test-runner:performance-tracking
requires:
- system-tests-node-modules-install
- system-tests-non-root:
context: test-runner:performance-tracking
executor: non-root-docker-user
Expand Down
2 changes: 0 additions & 2 deletions packages/resolve-dist/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,5 @@ export const getPathToIndex = (pkg: RunnerPkg) => {
}

export const getPathToDesktopIndex = (graphqlPort: number) => {
// For now, if we see that there's a CYPRESS_INTERNAL_VITE_DEV
// we assume we're running Cypress targeting that (dev server)
return `http://localhost:${graphqlPort}/__launchpad/index.html`
}
14 changes: 12 additions & 2 deletions packages/server/lib/browsers/cdp_automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { URL } from 'url'

import type { Automation } from '../automation'
import type { ResourceType, BrowserPreRequest, BrowserResponseReceived } from '@packages/proxy'
import type { WriteVideoFrame } from '@packages/types'

export type CdpCommand = keyof ProtocolMapping.Commands

Expand Down Expand Up @@ -168,9 +169,9 @@ export const normalizeResourceType = (resourceType: string | undefined): Resourc
return ffToStandardResourceTypeMap[resourceType] || 'other'
}

type SendDebuggerCommand = (message: CdpCommand, data?: any) => Promise<any>
type SendDebuggerCommand = <T extends CdpCommand>(message: T, data?: any) => Promise<ProtocolMapping.Commands[T]['returnType']>
type SendCloseCommand = (shouldKeepTabOpen: boolean) => Promise<any> | void
type OnFn = (eventName: CdpEvent, cb: Function) => void
type OnFn = <T extends CdpEvent>(eventName: T, cb: (data: ProtocolMapping.Events[T][0]) => void) => void

// the intersection of what's valid in CDP and what's valid in FFCDP
// Firefox: https://searchfox.org/mozilla-central/rev/98a9257ca2847fad9a19631ac76199474516b31e/remote/cdp/domains/parent/Network.jsm#22
Expand All @@ -188,6 +189,15 @@ export class CdpAutomation {
onFn('Network.responseReceived', this.onResponseReceived)
}

async startVideoRecording (writeVideoFrame: WriteVideoFrame, screencastOpts?) {
this.onFn('Page.screencastFrame', async (e) => {
writeVideoFrame(Buffer.from(e.data, 'base64'))
await this.sendDebuggerCommandFn('Page.screencastFrameAck', { sessionId: e.sessionId })
})

await this.sendDebuggerCommandFn('Page.startScreencast', screencastOpts)
}

static async create (sendDebuggerCommandFn: SendDebuggerCommand, onFn: OnFn, sendCloseCommandFn: SendCloseCommand, automation: Automation, experimentalSessionAndOrigin: boolean): Promise<CdpAutomation> {
const cdpAutomation = new CdpAutomation(sendDebuggerCommandFn, onFn, sendCloseCommandFn, automation, experimentalSessionAndOrigin)

Expand Down
54 changes: 24 additions & 30 deletions packages/server/lib/browsers/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ import { fs } from '../util/fs'
import { CdpAutomation, screencastOpts } from './cdp_automation'
import * as protocol from './protocol'
import utils from './utils'
import type { Browser } from './types'
import type { Browser, BrowserInstance } from './types'
import { BrowserCriClient } from './browser-cri-client'
import type { LaunchedBrowser } from '@packages/launcher/lib/browsers'
import type { CriClient } from './cri-client'
import type { Automation } from '../automation'
import type { BrowserLaunchOpts, BrowserNewTabOpts } from '@packages/types'
import type { BrowserLaunchOpts, BrowserNewTabOpts, RunModeVideoApi } from '@packages/types'

const debug = debugModule('cypress:server:browsers:chrome')

Expand Down Expand Up @@ -249,22 +248,12 @@ const _disableRestorePagesPrompt = function (userDir) {
.catch(() => { })
}

const _maybeRecordVideo = async function (client, options, browserMajorVersion) {
if (!options.onScreencastFrame) {
debug('options.onScreencastFrame is false')
async function _recordVideo (cdpAutomation: CdpAutomation, videoOptions: RunModeVideoApi, browserMajorVersion: number) {
const screencastOptions = browserMajorVersion >= CHROME_VERSION_WITH_FPS_INCREASE ? screencastOpts() : screencastOpts(1)

return client
}

debug('starting screencast')
client.on('Page.screencastFrame', (meta) => {
options.onScreencastFrame(meta)
client.send('Page.screencastFrameAck', { sessionId: meta.sessionId })
})

await client.send('Page.startScreencast', browserMajorVersion >= CHROME_VERSION_WITH_FPS_INCREASE ? screencastOpts() : screencastOpts(1))
const { writeVideoFrame } = await videoOptions.newFfmpegVideoController()

return client
await cdpAutomation.startVideoRecording(writeVideoFrame, screencastOptions)
}

// a utility function that navigates to the given URL
Expand Down Expand Up @@ -434,7 +423,9 @@ const _handlePausedRequests = async (client) => {
const _setAutomation = async (client: CriClient, automation: Automation, resetBrowserTargets: (shouldKeepTabOpen: boolean) => Promise<void>, options: BrowserLaunchOpts) => {
const cdpAutomation = await CdpAutomation.create(client.send, client.on, resetBrowserTargets, automation, !!options.experimentalSessionAndOrigin)

return automation.use(cdpAutomation)
automation.use(cdpAutomation)

return cdpAutomation
}

export = {
Expand All @@ -448,7 +439,7 @@ export = {

_removeRootExtension,

_maybeRecordVideo,
_recordVideo,

_navigateUsingCRI,

Expand All @@ -468,7 +459,7 @@ export = {
return browserCriClient
},

async _writeExtension (browser: Browser, options) {
async _writeExtension (browser: Browser, options: BrowserLaunchOpts) {
if (browser.isHeadless) {
debug('chrome is running headlessly, not installing extension')

Expand Down Expand Up @@ -562,10 +553,10 @@ export = {

if (!options.url) throw new Error('Missing url in connectToNewSpec')

await this.attachListeners(browser, options.url, pageCriClient, automation, options)
await this.attachListeners(options.url, pageCriClient, automation, options)
},

async connectToExisting (browser: Browser, options: BrowserLaunchOpts, automation) {
async connectToExisting (browser: Browser, options: BrowserLaunchOpts, automation: Automation) {
const port = await protocol.getRemoteDebuggingPort()

debug('connecting to existing chrome instance with url and debugging port', { url: options.url, port })
Expand All @@ -580,17 +571,19 @@ export = {
await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
},

async attachListeners (browser: Browser, url: string, pageCriClient, automation: Automation, options: BrowserLaunchOpts & { onInitializeNewBrowserTab?: () => void }) {
async attachListeners (url: string, pageCriClient: CriClient, automation: Automation, options: BrowserLaunchOpts | BrowserNewTabOpts) {
if (!browserCriClient) throw new Error('Missing browserCriClient in attachListeners')

await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
debug('attaching listeners to chrome %o', { url, options })

const cdpAutomation = await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)

await pageCriClient.send('Page.enable')

await options.onInitializeNewBrowserTab?.()
await options['onInitializeNewBrowserTab']?.()

await Promise.all([
this._maybeRecordVideo(pageCriClient, options, browser.majorVersion),
options.videoApi && this._recordVideo(cdpAutomation, options.videoApi, Number(options.browser.majorVersion)),
this._handleDownloads(pageCriClient, options.downloadsFolder, automation),
])

Expand All @@ -600,9 +593,11 @@ export = {
await this._handlePausedRequests(pageCriClient)
_listenForFrameTreeChanges(pageCriClient)
}

return cdpAutomation
},

async open (browser: Browser, url, options: BrowserLaunchOpts, automation: Automation): Promise<LaunchedBrowser> {
async open (browser: Browser, url, options: BrowserLaunchOpts, automation: Automation): Promise<BrowserInstance> {
const { isTextTerminal } = options

const userDir = utils.getProfileDir(browser, isTextTerminal)
Expand Down Expand Up @@ -654,7 +649,7 @@ export = {
// first allows us to connect the remote interface,
// start video recording and then
// we will load the actual page
const launchedBrowser = await launch(browser, 'about:blank', port, args) as LaunchedBrowser & { browserCriClient: BrowserCriClient}
const launchedBrowser = await launch(browser, 'about:blank', port, args) as unknown as BrowserInstance & { browserCriClient: BrowserCriClient}

la(launchedBrowser, 'did not get launched browser instance')

Expand All @@ -681,7 +676,6 @@ export = {

launchedBrowser.browserCriClient = browserCriClient

/* @ts-expect-error */
launchedBrowser.kill = (...args) => {
debug('closing remote interface client')

Expand All @@ -696,7 +690,7 @@ export = {

const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')

await this.attachListeners(browser, url, pageCriClient, automation, options)
await this.attachListeners(url, pageCriClient, automation, options)

// return the launched browser process
// with additional method to close the remote connection
Expand Down
Loading