mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-03 21:40:28 -05:00
chore: support experimentalSingleTabRunMode in protocol (#27659)
This commit is contained in:
@@ -362,7 +362,7 @@ export class CdpAutomation implements CDPClient {
|
||||
return false
|
||||
}
|
||||
|
||||
_handlePausedRequests = async (client) => {
|
||||
_handlePausedRequests = async (client: CriClient) => {
|
||||
// NOTE: only supported in chromium based browsers
|
||||
await client.send('Fetch.enable', {
|
||||
// only enable request pausing for documents to determine the AUT iframe
|
||||
@@ -390,7 +390,7 @@ export class CdpAutomation implements CDPClient {
|
||||
// we can't get the frame tree during the Fetch.requestPaused event, because
|
||||
// the CDP is tied up during that event and can't be utilized. so we maintain
|
||||
// a reference to it that's updated when it's likely to have been changed
|
||||
_listenForFrameTreeChanges = (client) => {
|
||||
_listenForFrameTreeChanges = (client: CriClient) => {
|
||||
debugVerbose('listen for frame tree changes')
|
||||
|
||||
client.on('Page.frameAttached', this._updateFrameTree(client, 'Page.frameAttached'))
|
||||
|
||||
@@ -443,6 +443,14 @@ export = {
|
||||
browserCriClient = undefined
|
||||
},
|
||||
|
||||
async connectProtocolToBrowser (options: { protocolManager?: ProtocolManagerShape }) {
|
||||
const browserCriClient = this._getBrowserCriClient()
|
||||
|
||||
if (!browserCriClient?.currentlyAttachedTarget) throw new Error('Missing pageCriClient in connectProtocolToBrowser')
|
||||
|
||||
await options.protocolManager?.connectToBrowser(browserCriClient.currentlyAttachedTarget)
|
||||
},
|
||||
|
||||
async connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) {
|
||||
debug('connecting to new chrome tab in existing instance with url and debugging port', { url: options.url })
|
||||
|
||||
@@ -456,7 +464,7 @@ export = {
|
||||
|
||||
if (!options.url) throw new Error('Missing url in connectToNewSpec')
|
||||
|
||||
await options.protocolManager?.connectToBrowser(pageCriClient)
|
||||
await this.connectProtocolToBrowser({ protocolManager: options.protocolManager })
|
||||
|
||||
await this.attachListeners(options.url, pageCriClient, automation, options)
|
||||
},
|
||||
|
||||
@@ -283,10 +283,15 @@ export = {
|
||||
this._clearCache(win.webContents),
|
||||
])
|
||||
|
||||
await browserCriClient?.currentlyAttachedTarget?.send('Page.enable')
|
||||
const browserCriClient = this._getBrowserCriClient()
|
||||
const pageCriClient = browserCriClient?.currentlyAttachedTarget
|
||||
|
||||
if (!pageCriClient) throw new Error('Missing pageCriClient in _launch')
|
||||
|
||||
await pageCriClient.send('Page.enable')
|
||||
|
||||
await Promise.all([
|
||||
protocolManager?.connectToBrowser(cdpAutomation),
|
||||
this.connectProtocolToBrowser({ protocolManager }),
|
||||
videoApi && recordVideo(cdpAutomation, videoApi),
|
||||
this._handleDownloads(win, options.downloadsFolder, automation),
|
||||
])
|
||||
@@ -296,14 +301,15 @@ export = {
|
||||
|
||||
await win.loadURL(url)
|
||||
|
||||
await cdpAutomation._handlePausedRequests(browserCriClient?.currentlyAttachedTarget)
|
||||
cdpAutomation._listenForFrameTreeChanges(browserCriClient?.currentlyAttachedTarget)
|
||||
await cdpAutomation._handlePausedRequests(pageCriClient)
|
||||
cdpAutomation._listenForFrameTreeChanges(pageCriClient)
|
||||
|
||||
return win
|
||||
},
|
||||
|
||||
_enableDebugger () {
|
||||
debug('debugger: enable Console and Network')
|
||||
const browserCriClient = this._getBrowserCriClient()
|
||||
|
||||
return browserCriClient?.currentlyAttachedTarget?.send('Console.enable')
|
||||
},
|
||||
@@ -333,6 +339,8 @@ export = {
|
||||
// avoid adding redundant `will-download` handlers if session is reused for next spec
|
||||
win.on('closed', () => session.removeListener('will-download', onWillDownload))
|
||||
|
||||
const browserCriClient = this._getBrowserCriClient()
|
||||
|
||||
return browserCriClient?.currentlyAttachedTarget?.send('Page.setDownloadBehavior', {
|
||||
behavior: 'allow',
|
||||
downloadPath: dir,
|
||||
@@ -370,6 +378,8 @@ export = {
|
||||
// set both because why not
|
||||
webContents.userAgent = userAgent
|
||||
|
||||
const browserCriClient = this._getBrowserCriClient()
|
||||
|
||||
// In addition to the session, also set the user-agent optimistically through CDP. @see https://github.com/cypress-io/cypress/issues/23597
|
||||
browserCriClient?.currentlyAttachedTarget?.send('Network.setUserAgentOverride', {
|
||||
userAgent,
|
||||
@@ -388,6 +398,10 @@ export = {
|
||||
})
|
||||
},
|
||||
|
||||
_getBrowserCriClient () {
|
||||
return browserCriClient
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear instance state for the electron instance, this is normally called on kill or on exit, for electron there isn't any state to clear.
|
||||
*/
|
||||
@@ -405,6 +419,14 @@ export = {
|
||||
throw new Error('Attempting to connect to existing browser for Cypress in Cypress which is not yet implemented for electron')
|
||||
},
|
||||
|
||||
async connectProtocolToBrowser (options: { protocolManager?: ProtocolManagerShape }) {
|
||||
const browserCriClient = this._getBrowserCriClient()
|
||||
|
||||
if (!browserCriClient?.currentlyAttachedTarget) throw new Error('Missing pageCriClient in connectProtocolToBrowser')
|
||||
|
||||
await options.protocolManager?.connectToBrowser(browserCriClient.currentlyAttachedTarget)
|
||||
},
|
||||
|
||||
validateLaunchOptions (launchOptions: typeof utils.defaultLaunchOptions) {
|
||||
const options: string[] = []
|
||||
|
||||
|
||||
@@ -390,6 +390,10 @@ export function connectToExisting () {
|
||||
getCtx().onWarning(getError('UNEXPECTED_INTERNAL_ERROR', new Error('Attempting to connect to existing browser for Cypress in Cypress which is not yet implemented for firefox')))
|
||||
}
|
||||
|
||||
export function connectProtocolToBrowser (): Promise<void> {
|
||||
throw new Error('Protocol is not yet supported in firefox.')
|
||||
}
|
||||
|
||||
async function recordVideo (videoApi: RunModeVideoApi) {
|
||||
const { writeVideoFrame } = await videoApi.useFfmpegVideoController({ webmInput: true })
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import check from 'check-more-types'
|
||||
import { exec } from 'child_process'
|
||||
import util from 'util'
|
||||
import os from 'os'
|
||||
import { BROWSER_FAMILY, BrowserLaunchOpts, BrowserNewTabOpts, FoundBrowser } from '@packages/types'
|
||||
import { BROWSER_FAMILY, BrowserLaunchOpts, BrowserNewTabOpts, FoundBrowser, ProtocolManagerShape } from '@packages/types'
|
||||
import type { Browser, BrowserInstance, BrowserLauncher } from './types'
|
||||
import type { Automation } from '../automation'
|
||||
|
||||
@@ -137,6 +137,12 @@ export = {
|
||||
return this.getBrowserInstance()
|
||||
},
|
||||
|
||||
async connectProtocolToBrowser (options: { browser: Browser, foundBrowsers?: FoundBrowser[], protocolManager?: ProtocolManagerShape }) {
|
||||
const browserLauncher = await getBrowserLauncher(options.browser, options.foundBrowsers || [])
|
||||
|
||||
await browserLauncher.connectProtocolToBrowser(options)
|
||||
},
|
||||
|
||||
async connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation): Promise<BrowserInstance | null> {
|
||||
const browserLauncher = await getBrowserLauncher(browser, options.browsers)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FoundBrowser, BrowserLaunchOpts, BrowserNewTabOpts } from '@packages/types'
|
||||
import type { FoundBrowser, BrowserLaunchOpts, BrowserNewTabOpts, ProtocolManagerShape } from '@packages/types'
|
||||
import type { EventEmitter } from 'events'
|
||||
import type { Automation } from '../automation'
|
||||
|
||||
@@ -39,4 +39,8 @@ export type BrowserLauncher = {
|
||||
* Used to clear instance state after the browser has been exited.
|
||||
*/
|
||||
clearInstanceState: () => void
|
||||
/**
|
||||
* Used to connect the protocol to an existing browser.
|
||||
*/
|
||||
connectProtocolToBrowser: (options: { protocolManager?: ProtocolManagerShape }) => Promise<void>
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ export function connectToExisting () {
|
||||
throw new Error('Cypress-in-Cypress is not supported for WebKit.')
|
||||
}
|
||||
|
||||
export function connectProtocolToBrowser (): Promise<void> {
|
||||
throw new Error('Protocol is not yet supported in WebKit.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Playwright adds an `exit` event listener to run a cleanup process. It tries to use the current binary to run a Node script by passing it as argv[1].
|
||||
* However, the Electron binary does not support an entrypoint, leading Cypress to think it's being opened in global mode (no args) when this fn is called.
|
||||
|
||||
@@ -464,7 +464,7 @@ function listenForProjectEnd (project, exit): Bluebird<any> {
|
||||
async function waitForBrowserToConnect (options: { project: Project, socketId: string, onError: (err: Error) => void, spec: SpecWithRelativeRoot, isFirstSpecInBrowser: boolean, testingType: string, experimentalSingleTabRunMode: boolean, browser: Browser, screenshots: ScreenshotMetadata[], projectRoot: string, shouldLaunchNewTab: boolean, webSecurity: boolean, videoRecording?: VideoRecording, protocolManager?: ProtocolManager }) {
|
||||
if (globalThis.CY_TEST_MOCK?.waitForBrowserToConnect) return Promise.resolve()
|
||||
|
||||
const { project, socketId, onError, spec } = options
|
||||
const { project, socketId, onError, spec, browser, protocolManager } = options
|
||||
const browserTimeout = Number(process.env.CYPRESS_INTERNAL_BROWSER_CONNECT_TIMEOUT || 60000)
|
||||
let browserLaunchAttempt = 1
|
||||
|
||||
@@ -492,6 +492,12 @@ async function waitForBrowserToConnect (options: { project: Project, socketId: s
|
||||
openProject.updateTelemetryContext(JSON.stringify(telemetry.getActiveContextObject()))
|
||||
}
|
||||
|
||||
// since we aren't going to be opening a new tab,
|
||||
// we need to tell the protocol manager to reconnect to the existing browser
|
||||
if (protocolManager) {
|
||||
await openProject.connectProtocolToBrowser({ browser, foundBrowsers: project.options.browsers, protocolManager })
|
||||
}
|
||||
|
||||
// since we aren't re-launching the browser, we have to navigate to the next spec instead
|
||||
debug('navigating to next spec %s', createPublicSpec(spec))
|
||||
|
||||
|
||||
@@ -229,6 +229,10 @@ export class OpenProject {
|
||||
return this.closeOpenProjectAndBrowsers()
|
||||
}
|
||||
|
||||
async connectProtocolToBrowser (options) {
|
||||
await browsers.connectProtocolToBrowser(options)
|
||||
}
|
||||
|
||||
changeUrlToSpec (spec: Cypress.Spec) {
|
||||
if (!this.projectBase) {
|
||||
debug('No projectBase, cannot change url')
|
||||
|
||||
@@ -491,6 +491,10 @@ describe('lib/browsers/chrome', () => {
|
||||
|
||||
context('#connectToNewSpec', () => {
|
||||
it('launches a new tab, connects a cri client to it, starts video, navigates to the spec url, and handles downloads', async function () {
|
||||
const protocolManager = {
|
||||
connectToBrowser: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
const pageCriClient = {
|
||||
send: sinon.stub().resolves(),
|
||||
on: sinon.stub(),
|
||||
@@ -521,6 +525,7 @@ describe('lib/browsers/chrome', () => {
|
||||
onInitializeNewBrowserTab: () => {
|
||||
onInitializeNewBrowserTabCalled = true
|
||||
},
|
||||
protocolManager,
|
||||
}
|
||||
|
||||
sinon.stub(chrome, '_getBrowserCriClient').returns(browserCriClient)
|
||||
@@ -536,6 +541,53 @@ describe('lib/browsers/chrome', () => {
|
||||
expect(chrome._navigateUsingCRI).to.be.called
|
||||
expect(chrome._handleDownloads).to.be.called
|
||||
expect(onInitializeNewBrowserTabCalled).to.be.true
|
||||
expect(protocolManager.connectToBrowser).to.be.calledWith(pageCriClient)
|
||||
})
|
||||
})
|
||||
|
||||
context('#connectProtocolToBrowser', () => {
|
||||
it('connects to the browser cri client', async function () {
|
||||
const protocolManager = {
|
||||
connectToBrowser: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
const pageCriClient = sinon.stub()
|
||||
|
||||
const browserCriClient = {
|
||||
currentlyAttachedTarget: pageCriClient,
|
||||
}
|
||||
|
||||
sinon.stub(chrome, '_getBrowserCriClient').returns(browserCriClient)
|
||||
|
||||
await chrome.connectProtocolToBrowser({ protocolManager })
|
||||
|
||||
expect(protocolManager.connectToBrowser).to.be.calledWith(pageCriClient)
|
||||
})
|
||||
|
||||
it('throws error if there is no browser cri client', function () {
|
||||
const protocolManager = {
|
||||
connectToBrowser: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
sinon.stub(chrome, '_getBrowserCriClient').returns(null)
|
||||
|
||||
expect(chrome.connectProtocolToBrowser({ protocolManager })).to.be.rejectedWith('Missing pageCriClient in connectProtocolToBrowser')
|
||||
expect(protocolManager.connectToBrowser).not.to.be.called
|
||||
})
|
||||
|
||||
it('throws error if there is no page cri client', function () {
|
||||
const protocolManager = {
|
||||
connectToBrowser: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
const browserCriClient = {
|
||||
currentlyAttachedTarget: null,
|
||||
}
|
||||
|
||||
sinon.stub(chrome, '_getBrowserCriClient').returns(browserCriClient)
|
||||
|
||||
expect(chrome.connectProtocolToBrowser({ protocolManager })).to.be.rejectedWith('Missing pageCriClient in connectProtocolToBrowser')
|
||||
expect(protocolManager.connectToBrowser).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ const ELECTRON_PID = 10001
|
||||
|
||||
describe('lib/browsers/electron', () => {
|
||||
beforeEach(function () {
|
||||
this.protocolManager = {
|
||||
connectToBrowser: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
this.url = 'https://foo.com'
|
||||
this.state = {}
|
||||
this.options = {
|
||||
@@ -203,6 +207,31 @@ describe('lib/browsers/electron', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('.connectProtocolToBrowser', () => {
|
||||
it('connects to the browser cri client', async function () {
|
||||
sinon.stub(electron, '_getBrowserCriClient').returns(this.browserCriClient)
|
||||
|
||||
await electron.connectProtocolToBrowser({ protocolManager: this.protocolManager })
|
||||
expect(this.protocolManager.connectToBrowser).to.be.calledWith(this.pageCriClient)
|
||||
})
|
||||
|
||||
it('throws error if there is no browser cri client', function () {
|
||||
sinon.stub(electron, '_getBrowserCriClient').returns(null)
|
||||
|
||||
expect(electron.connectProtocolToBrowser({ protocolManager: this.protocolManager })).to.be.rejectedWith('Missing pageCriClient in connectProtocolToBrowser')
|
||||
expect(this.protocolManager.connectToBrowser).not.to.be.called
|
||||
})
|
||||
|
||||
it('throws error if there is no page cri client', async function () {
|
||||
this.browserCriClient.currentlyAttachedTarget = null
|
||||
|
||||
sinon.stub(electron, '_getBrowserCriClient').returns(this.browserCriClient)
|
||||
|
||||
expect(electron.connectProtocolToBrowser({ protocolManager: this.protocolManager })).to.be.rejectedWith('Missing pageCriClient in connectProtocolToBrowser')
|
||||
expect(this.protocolManager.connectToBrowser).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
context('._launch', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(menu, 'set')
|
||||
@@ -457,6 +486,12 @@ describe('lib/browsers/electron', () => {
|
||||
|
||||
expect(this.pageCriClient.send).to.be.calledWith('Page.getFrameTree')
|
||||
})
|
||||
|
||||
it('connects the protocol manager to the browser', async function () {
|
||||
await electron._launch(this.win, this.url, this.automation, this.options, undefined, this.protocolManager)
|
||||
|
||||
expect(this.protocolManager.connectToBrowser).to.be.calledWith(this.pageCriClient)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -434,6 +434,12 @@ describe('lib/browsers/firefox', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('#connectProtocolToBrowser', () => {
|
||||
it('throws error', () => {
|
||||
expect(firefox.connectProtocolToBrowser).to.throw('Protocol is not yet supported in firefox.')
|
||||
})
|
||||
})
|
||||
|
||||
context('firefox-util', () => {
|
||||
context('#setupMarionette', () => {
|
||||
// @see https://github.com/cypress-io/cypress/issues/7159
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
require('../../spec_helper')
|
||||
|
||||
import { expect } from 'chai'
|
||||
|
||||
import * as webkit from '../../../lib/browsers/webkit'
|
||||
|
||||
describe('lib/browsers/webkit', () => {
|
||||
context('#connectProtocolToBrowser', () => {
|
||||
it('throws error', () => {
|
||||
expect(webkit.connectProtocolToBrowser).to.throw('Protocol is not yet supported in WebKit.')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -255,4 +255,15 @@ describe('lib/open_project', () => {
|
||||
expect(ProjectBase.prototype.sendFocusBrowserMessage).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
context('#connectProtocolToBrowser', () => {
|
||||
it('connects protocol to browser', async () => {
|
||||
sinon.stub(browsers, 'connectProtocolToBrowser').resolves()
|
||||
const options = sinon.stub()
|
||||
|
||||
await openProject.connectProtocolToBrowser(options)
|
||||
|
||||
expect(browsers.connectProtocolToBrowser).to.be.calledWith(options)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
projectId: 'pid123',
|
||||
e2e: {
|
||||
supportFile: false,
|
||||
setupNodeEvents: (on, config) => {},
|
||||
},
|
||||
component: {
|
||||
devServer: {
|
||||
framework: 'react',
|
||||
bundler: 'webpack',
|
||||
webpackConfig: {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx|ts|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
+2
@@ -14,6 +14,8 @@ describe('protocol events', { baseUrl: 'http://foobar.com:2121' }, () => {
|
||||
cy.origin('http://foobar.com', () => {
|
||||
// verify changing the viewport inside cy.origin works
|
||||
cy.viewport(400, 500)
|
||||
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(1000, { log: false })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
/* eslint-disable cypress/no-unnecessary-waiting */
|
||||
|
||||
describe('test isolation', { baseUrl: null }, () => {
|
||||
describe('test isolation false', { testIsolation: false }, () => {
|
||||
it('test 1', () => {
|
||||
cy.visit('cypress/fixtures/dom-with-browser-interactions.html')
|
||||
cy.wait(1000, { log: false })
|
||||
cy.get('#text-target').type('abc').should('have.value', 'abc')
|
||||
})
|
||||
|
||||
it('test 2', () => {
|
||||
cy.get('#text-target').type('def').should('have.value', 'abcdef')
|
||||
})
|
||||
|
||||
it('test 3', () => {
|
||||
cy.get('#text-target').type('ghi').should('have.value', 'abcdefghi')
|
||||
})
|
||||
|
||||
it('test 4', () => {
|
||||
cy.get('#text-target').type('!').should('have.value', 'abcdefghi!')
|
||||
|
||||
cy.visit('cypress/fixtures/dom-with-browser-interactions.html')
|
||||
cy.wait(1000, { log: false })
|
||||
cy.get('#text-target').type('abc').should('have.value', 'abc')
|
||||
})
|
||||
|
||||
it('test 5', () => {
|
||||
cy.get('#text-target').type('!').should('have.value', 'abc!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('test isolation true', { testIsolation: true }, () => {
|
||||
it('test 6', () => {
|
||||
cy.visit('cypress/fixtures/dom-with-browser-interactions.html')
|
||||
cy.wait(1000, { log: false })
|
||||
cy.get('#text-target').type('abc').should('have.value', 'abc')
|
||||
})
|
||||
|
||||
it('test 7', () => {
|
||||
cy.visit('cypress/fixtures/dom-with-browser-interactions.html')
|
||||
cy.wait(1000, { log: false })
|
||||
cy.get('#text-target').type('def').should('have.value', 'def')
|
||||
})
|
||||
})
|
||||
|
||||
describe('test isolation false', { testIsolation: false }, () => {
|
||||
it('test 8', () => {
|
||||
cy.get('#text-target').type('abc').should('have.value', 'defabc')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>DOM with Browser Interactions Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="dom">
|
||||
<h1>DOM with Browser Interactions Fixture</h1>
|
||||
<p>Fixture for browser interaction tests.</p>
|
||||
<input type="text" id="text-target" placeholder="Input text">
|
||||
<select name="select" id="select-target">
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
</select>
|
||||
<select name="select-multi" id="select-target-multi" multiple>
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
</select>
|
||||
<div>
|
||||
<input type="radio" name="radio-inputs" id="radio-1" />
|
||||
<input type="radio" name="radio-inputs" id="radio-2" />
|
||||
</div>
|
||||
<input type="checkbox" name="check" id="check-target" />
|
||||
<div style="height:10000px; position: relative;">
|
||||
<div id="scroll-target-window" style="position:absolute;bottom:0px;height:100px;width:100px;overflow:scroll;background:#ccc">
|
||||
<div id="spacer" style="height:100px;width:100px;background:#cff"></div>
|
||||
<div id="scroll-target-element" style="background:#fcf;height:100px;width:100px;margin-left:100px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-cy-root></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
import { mount } from 'cypress/react'
|
||||
|
||||
// Augment the Cypress namespace to include type definitions for
|
||||
// your custom command.
|
||||
// Alternatively, can be defined in cypress/support/component.d.ts
|
||||
// with a <reference path="./component" /> at the top of your spec.
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
mount: typeof mount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add('mount', mount)
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>hi</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "protocol-sample-project",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.8",
|
||||
"@babel/preset-env": "^7.17.8",
|
||||
"@babel/preset-react": "^7.17.8",
|
||||
"@babel/preset-typescript": "^7.17.8",
|
||||
"css-loader": "^6.5.1",
|
||||
"esbuild": "^0.17.11",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.65.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"plugin:cypress/recommended"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
body {
|
||||
background-color: #282c34;
|
||||
color: white;
|
||||
padding: 40px;
|
||||
font-family: Arial;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import HelloEarth from './HelloEarth.jsx'
|
||||
|
||||
describe('<HelloEarth />', () => {
|
||||
it('test 1', () => {
|
||||
cy.mount(<HelloEarth />)
|
||||
cy.get('#earth-text')
|
||||
.type('Hello Earth')
|
||||
.should('have.value', 'Hello Earth')
|
||||
})
|
||||
|
||||
it('test 2', () => {
|
||||
cy.mount(<HelloEarth />)
|
||||
cy.get('#earth-text')
|
||||
.type('Where\'s Mars?')
|
||||
.should('have.value', 'Where\'s Mars?')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import './Earth.module.css'
|
||||
|
||||
export default class HelloEarth extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello Earth</h1>
|
||||
<input type="text" id="earth-text" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import HelloMars from './HelloMars.jsx'
|
||||
|
||||
describe('<HelloMars />', () => {
|
||||
it('test 1', () => {
|
||||
cy.mount(<HelloMars />)
|
||||
cy.get('#mars-text')
|
||||
.type('Hello Mars')
|
||||
.should('have.value', 'Hello Mars')
|
||||
})
|
||||
|
||||
it('test 2', () => {
|
||||
cy.mount(<HelloMars />)
|
||||
cy.get('#mars-text')
|
||||
.type('Where\'s Earth?')
|
||||
.should('have.value', 'Where\'s Earth?')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import './Mars.module.css'
|
||||
|
||||
export default class HelloMars extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello Mars</h1>
|
||||
<input type="text" id="mars-text" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
body {
|
||||
background-color: #282c34;
|
||||
color: white;
|
||||
padding: 40px;
|
||||
font-family: Arial;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": ["es6", "dom"],
|
||||
"types": ["cypress", "node"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ const {
|
||||
|
||||
// source: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
|
||||
const isoDateRegex = /"([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?"/g
|
||||
const numberRegex = /"(wallClockDuration|fnDuration|afterFnDuration|lifecycle|duration|timestamp|createdAtTimestamp|updatedAtTimestamp)": (0|[1-9]\d*)(\.\d+)?/g
|
||||
const numberRegex = /"(wallClockDuration|fnDuration|afterFnDuration|lifecycle|duration|timestamp|createdAtTimestamp|updatedAtTimestamp|x|y|top|left|topCenter|leftCenter)": (0|[1-9]\d*)(\.\d+)?/g
|
||||
const pathRegex = /"(name|absoluteFile)": "\/[^"]+"/g
|
||||
|
||||
const normalizeEvents = (resultsJson) => {
|
||||
@@ -22,7 +22,7 @@ const normalizeEvents = (resultsJson) => {
|
||||
|
||||
const getFilePath = (filename) => {
|
||||
return path.join(
|
||||
Fixtures.projectPath('e2e'),
|
||||
Fixtures.projectPath('protocol'),
|
||||
'cypress',
|
||||
'system-tests-protocol-dbs',
|
||||
`${filename}.json`,
|
||||
@@ -33,12 +33,11 @@ describe('capture-protocol', () => {
|
||||
setupStubbedServer(createRoutes())
|
||||
enableCaptureProtocol()
|
||||
|
||||
describe('passing', () => {
|
||||
describe('e2e', () => {
|
||||
it('verifies the protocol events are correct', function () {
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
configFile: 'cypress-with-project-id.config.js',
|
||||
spec: 'protocol.cy.js',
|
||||
project: 'protocol',
|
||||
record: true,
|
||||
expectedExitCode: 0,
|
||||
port: 2121,
|
||||
@@ -50,10 +49,37 @@ describe('capture-protocol', () => {
|
||||
}).then(() => {
|
||||
const protocolEvents = fs.readFileSync(getFilePath('e9e81b5e-cc58-4026-b2ff-8ae3161435a6.db'), 'utf8')
|
||||
|
||||
systemTests.snapshot('protocol events', normalizeEvents(protocolEvents))
|
||||
systemTests.snapshot('e2e events', normalizeEvents(protocolEvents))
|
||||
|
||||
fs.removeSync(getFilePath('e9e81b5e-cc58-4026-b2ff-8ae3161435a6.db'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: skip for now because the component tests are failing with the new route consolidation logic
|
||||
describe.skip('component', () => {
|
||||
[true, false].forEach((experimentalSingleTabRunMode) => {
|
||||
it('verifies the protocol events are correct', function () {
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
project: 'protocol',
|
||||
record: true,
|
||||
expectedExitCode: 0,
|
||||
testingType: 'component',
|
||||
port: 2121,
|
||||
config: {
|
||||
component: {
|
||||
experimentalSingleTabRunMode,
|
||||
},
|
||||
},
|
||||
}).then(() => {
|
||||
const protocolEvents = fs.readFileSync(getFilePath('e9e81b5e-cc58-4026-b2ff-8ae3161435a6.db'), 'utf8')
|
||||
|
||||
systemTests.snapshot(`component events - experimentalSingleTabRunMode: ${experimentalSingleTabRunMode}`, normalizeEvents(protocolEvents))
|
||||
|
||||
fs.removeSync(getFilePath('e9e81b5e-cc58-4026-b2ff-8ae3161435a6.db'))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user