Merge remote-tracking branch 'origin' into 7.0-release

This commit is contained in:
Jessica Sachs
2021-04-01 18:26:10 -04:00
29 changed files with 215 additions and 44 deletions

View File

@@ -67,6 +67,11 @@ describe('Footer', () => {
it('shows update indicator', () => {
cy.get('.update-indicator').should('be.visible')
cy.wrap(ipc.updaterCheck).should('be.calledOnceWith', {
initialLaunch: true,
testingType: 'e2e',
})
cy.percySnapshot()
})

View File

@@ -5,16 +5,23 @@ import updateStore from '../update/update-store'
import ipc from '../lib/ipc'
import { useLifecycle } from '../lib/use-lifecycle'
let initialLaunch = true
const checkForUpdate = () => {
ipc.offUpdaterCheck()
ipc.updaterCheck()
ipc.updaterCheck({
initialLaunch,
testingType: 'e2e',
})
.then((version) => {
if (version) updateStore.setNewVersion(version)
})
.catch((error) => {
console.warn('Error checking for updates:', error) // eslint-disable-line no-console
})
initialLaunch = false
}
export const useUpdateChecker = () => {

View File

@@ -0,0 +1,29 @@
<html>
<body>
<div id="parent" style="display:flex; height: 90vh;">
<div id="Preselect" style="flex:1; background-color:aquamarine; height: 100%;">
Preselect
</div>
<div id="Details" style="flex:1; background-color:blue; height: 100%; overflow:scroll;">
Details
<div id="modal" style="background-color: chartreuse; height: 80vh; width: 90%; position: fixed; display: block; left:5%; top:5%;">
<div id="sticky" style="background-color: darkgray; position:sticky; bottom: 10%; width:9%; height:10%;">
<button id="button" style="background-color: darkred; color:white;">
click me
</button>
<div id="message"></div>
</div>
</div>
</div>
</div>
</body>
<script>
const btn = document.getElementById('button')
const msg = document.getElementById('message')
btn.onclick = (e) => {
msg.innerHTML = 'Success!'
}
</script>
</html>

View File

@@ -780,6 +780,11 @@ describe('src/cypress/dom/visibility', () => {
expect(this.$childPointerEventsNone.find('span')).to.be.visible
expect(this.$childPointerEventsNone.find('span')).to.not.be.hidden
})
it('is visible when position: sticky', () => {
cy.visit('fixtures/sticky.html')
cy.get('#button').should('be.visible')
})
})
describe('css display', function () {

View File

@@ -87,7 +87,7 @@ const isHidden = (el, methodName = 'isHidden()', options = { checkOpacity: true
return true // is hidden
}
if (elOrAncestorIsFixed($el)) {
if (elOrAncestorIsFixedOrSticky($el)) {
return elIsNotElementFromPoint($el)
}
@@ -218,12 +218,8 @@ const canClipContent = function ($el, $ancestor) {
return true
}
const elOrAncestorIsFixed = function ($el) {
const $stickyOrFixedEl = $elements.getFirstFixedOrStickyPositionParent($el)
if ($stickyOrFixedEl) {
return $stickyOrFixedEl.css('position') === 'fixed'
}
const elOrAncestorIsFixedOrSticky = function ($el) {
return !!$elements.getFirstFixedOrStickyPositionParent($el)
}
const elAtCenterPoint = function ($el) {
@@ -509,7 +505,7 @@ const getReasonIsHidden = function ($el, options = { checkOpacity: true }) {
}
// nested else --___________--
if (elOrAncestorIsFixed($el)) {
if (elOrAncestorIsFixedOrSticky($el)) {
if (elIsNotElementFromPoint($el)) {
// show the long element here
const covered = $elements.stringify(elAtCenterPoint($el))

View File

@@ -1,18 +1,40 @@
/* eslint-disable no-console */
import chalk from 'chalk'
import browsers from '@packages/server/lib/browsers'
import openProject from '@packages/server/lib/open_project'
export * from './src/socket-ct'
export * from './src/server-ct'
import chalk from 'chalk'
import human from 'human-interval'
import _ from 'lodash'
export * from './src/project-ct'
export * from './src/server-ct'
export * from './src/socket-ct'
export * from './src/specs-store'
const Updater = require('@packages/server/lib/updater')
const registerCheckForUpdates = () => {
const checkForUpdates = (initialLaunch) => {
Updater.check({
initialLaunch,
testingType: 'ct',
onNewVersion: _.noop,
onNoNewVersion: _.noop,
})
}
setInterval(() => checkForUpdates(false), human('60 minutes'))
checkForUpdates(true)
}
// Partial because there are probably other options that are not included in this type.
export const start = async (projectRoot: string, args: Record<string, any>) => {
if (process.env['CYPRESS_INTERNAL_ENV'] === 'production') {
registerCheckForUpdates()
}
// add chrome as a default browser if none has been specified
return browsers.ensureAndGetByNameOrPath(args.browser)
.then((browser: Cypress.Browser) => {

View File

@@ -0,0 +1,42 @@
const Updater = require('@packages/server/lib/updater')
import openProject from '@packages/server/lib/open_project'
import browsers from '@packages/server/lib/browsers'
import sinon from 'sinon'
import { expect } from 'chai'
import * as Index from '../../index'
describe('index.spec', () => {
let backupEnv
let stub_setInterval
beforeEach(() => {
backupEnv = process.env
process.env.CYPRESS_INTERNAL_ENV = 'production'
stub_setInterval = sinon.spy(global, 'setInterval')
sinon.stub(Updater, 'check').resolves()
sinon.stub(openProject, 'create').resolves()
sinon.stub(openProject, 'launch').resolves()
sinon.stub(browsers, 'ensureAndGetByNameOrPath').resolves()
})
afterEach(() => {
if (backupEnv) {
process.env = backupEnv
backupEnv = null
}
})
it('registers update check', async () => {
await Index.start('/path/to/project', {
browser: 'chrome',
})
expect(stub_setInterval.callCount).eq(1)
expect(stub_setInterval.firstCall.args[1]).eq(1000 * 60 * 60)
expect(Updater.check.callCount).eq(1)
expect(Updater.check.firstCall.args[0]).includes({
testingType: 'ct',
initialLaunch: true,
})
})
})

View File

@@ -34,6 +34,6 @@ process.noDeprecation = process.env.CYPRESS_INTERNAL_ENV === 'production'
// always show stack traces for Electron deprecation warnings
process.traceDeprecation = true
require('./lib/util/suppress_unauthorized_warning').suppress()
require('./lib/util/suppress_warnings').suppress()
module.exports = require('./lib/cypress').start(process.argv)

View File

@@ -261,6 +261,7 @@ module.exports = {
'recordKey',
'specPattern',
'tags',
'testingType',
]),
runnerCapabilities,
}

View File

@@ -235,7 +235,7 @@ module.exports = {
return this._enableDebugger(win.webContents)
})
.then(() => {
return this._handleDownloads(win.webContents, options.downloadsFolder, automation)
return this._handleDownloads(win, options.downloadsFolder, automation)
})
.return(win)
},
@@ -291,8 +291,8 @@ module.exports = {
return webContents.debugger.sendCommand('Console.enable')
},
_handleDownloads (webContents, dir, automation) {
webContents.session.on('will-download', (event, downloadItem) => {
_handleDownloads (win, dir, automation) {
const onWillDownload = (event, downloadItem) => {
const savePath = path.join(dir, downloadItem.getFilename())
automation.push('create:download', {
@@ -307,9 +307,16 @@ module.exports = {
id: downloadItem.getETag(),
})
})
})
}
return webContents.debugger.sendCommand('Page.setDownloadBehavior', {
const { session } = win.webContents
session.on('will-download', onWillDownload)
// avoid adding redundant `will-download` handlers if session is reused for next spec
win.on('closed', () => session.removeListener('will-download', onWillDownload))
return win.webContents.debugger.sendCommand('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: dir,
})

View File

@@ -202,6 +202,7 @@ const handleEvent = function (options, bus, event, id, type, arg) {
case 'updater:check':
return Updater.check({
...arg,
onNewVersion ({ version }) {
return send(version)
},

View File

@@ -273,7 +273,7 @@ const createRun = Promise.method((options = {}) => {
ciBuildId: null,
})
let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags } = options
let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType } = options
if (recordKey == null) {
recordKey = env.get('CYPRESS_RECORD_KEY')
@@ -319,6 +319,7 @@ const createRun = Promise.method((options = {}) => {
projectId,
recordKey,
specPattern,
testingType,
ci: {
params: ciProvider.ciParams(),
provider: ciProvider.provider(),

View File

@@ -1,6 +1,6 @@
require('graceful-fs').gracefulify(require('fs'))
require('../../util/suppress_unauthorized_warning').suppress()
require('../../util/suppress_warnings').suppress()
const ipc = require('../util').wrapIpc(process)
const { file: pluginsFile, projectRoot } = require('minimist')(process.argv.slice(2))

View File

@@ -19,6 +19,10 @@ module.exports = {
return emitter.emit(message.event, ...message.args)
})
// prevent max listeners warning on ipc
// @see https://github.com/cypress-io/cypress/issues/1305#issuecomment-780895569
emitter.setMaxListeners(Infinity)
return {
send (event, ...args) {
if (aProcess.killed) {

View File

@@ -23,7 +23,7 @@ import statusCode from './util/status_code'
type WarningErr = Record<string, any>
const fullyQualifiedRe = /^https?:\/\//
const textHtmlContentTypeRe = /^text\/html/i
const htmlContentTypesRe = /^(text\/html|application\/xhtml)/i
const debug = Debug('cypress:server:server-e2e')
@@ -32,7 +32,7 @@ const isResponseHtml = function (contentType, responseBuffer) {
// want to match anything starting with 'text/html'
// including 'text/html;charset=utf-8' and 'Text/HTML'
// https://github.com/cypress-io/cypress/issues/8506
return textHtmlContentTypeRe.test(contentType)
return htmlContentTypesRe.test(contentType)
}
const body = _.invoke(responseBuffer, 'toString')

View File

@@ -1,3 +1,4 @@
const os = require('os')
const debug = require('debug')('cypress:server:updater')
const semver = require('semver')
const rp = require('@cypress/request-promise')
@@ -5,15 +6,17 @@ const pkg = require('@packages/root')
const { agent } = require('@packages/network')
const konfig = require('./konfig')
const { machineId } = require('./util/machine_id')
const _getManifest = (id) => {
const _getManifest = ({ id, initialLaunch, testingType }) => {
const url = konfig('desktop_manifest_url')
return rp.get({
url,
headers: {
'x-cypress-version': pkg.version,
'x-os-name': os.platform(),
'x-machine-id': id,
'x-initial-launch:': String(initialLaunch),
'x-testing-type': testingType,
},
agent,
proxy: null,
@@ -21,10 +24,14 @@ const _getManifest = (id) => {
})
}
const check = async ({ onNewVersion, onNoNewVersion } = {}) => {
const check = async ({ testingType, initialLaunch, onNewVersion, onNoNewVersion } = {}) => {
try {
const id = await machineId()
const manifest = await _getManifest(id)
const manifest = await _getManifest({
id,
testingType,
initialLaunch,
})
if (!manifest || !manifest.version) {
throw new Error('manifest is empty or does not have a version')

View File

@@ -1,6 +1,7 @@
const _ = require('lodash')
const Debug = require('debug')
const originalEmitWarning = process.emitWarning
const debug = Debug('cypress:server:lib:util:suppress_warnings')
let suppressed = false
@@ -10,7 +11,8 @@ let suppressed = false
* https://github.com/cypress-io/cypress/issues/5248
*/
const suppress = () => {
if (suppressed) {
if (suppressed || process.env.CYPRESS_INTERNAL_ENV === 'production') {
// in development, still log warnings since they are helpful
return
}
@@ -18,7 +20,7 @@ const suppress = () => {
process.emitWarning = (warning, type, code, ...args) => {
if (_.isString(warning) && _.includes(warning, 'NODE_TLS_REJECT_UNAUTHORIZED')) {
// https://github.com/nodejs/node/blob/82f89ec8c1554964f5029fab1cf0f4fad1fa55a8/lib/_tls_wrap.js#L1378-L1384
// https://github.com/nodejs/node/blob/85e6089c4db4da23dd88358fe0a12edefcd411f2/lib/internal/options.js#L17
return
}
@@ -31,7 +33,7 @@ const suppress = () => {
return
}
return originalEmitWarning.call(process, warning, type, code, ...args)
debug('suppressed emitWarning from node: %o', { process, warning, type, code, args })
}
}

View File

@@ -127,7 +127,7 @@
"@babel/core": "7.9.0",
"@babel/preset-env": "7.9.0",
"@cypress/debugging-proxy": "2.0.1",
"@cypress/json-schemas": "5.37.3",
"@cypress/json-schemas": "5.38.0",
"@cypress/sinon-chai": "2.9.1",
"@ffprobe-installer/ffprobe": "1.1.0",
"@packages/desktop-gui": "0.0.0-development",

View File

@@ -0,0 +1,33 @@
import _ from 'lodash'
import path from 'path'
import fs from 'fs-extra'
import e2e from '../support/helpers/e2e'
import Fixtures from '../support/helpers/fixtures'
const projectPath = Fixtures.projectPath('max-listeners')
describe('max listeners warning spec', () => {
e2e.setup()
// @see https://github.com/cypress-io/cypress/issues/1305
e2e.it('does not log MaxEventListeners error', {
browser: 'electron',
project: projectPath,
spec: '*',
onRun: async (exec) => {
const integrationPath = path.join(projectPath, 'cypress/integration')
// create a bunch of dummy test files to reproduce #1305
await fs.mkdirp(integrationPath)
await Promise.all(
_.times(15, (i) => fs.writeFile(path.join(integrationPath, `${i}.spec.js`), `it('test', () => {})`)),
)
const { stderr } = await exec()
expect(stderr).to.not.include('setMaxListeners')
expect(stderr).to.not.include('preprocessor:close')
},
})
})

View File

@@ -100,6 +100,7 @@ describe('e2e record', () => {
expect(postRun.body.projectId).to.eq('pid123')
expect(postRun.body.recordKey).to.eq('f858a2bc-b469-4e48-be67-0876339ee7e1')
expect(postRun.body.specPattern).to.eq('cypress/integration/record*')
expect(postRun.body.testingType).to.eq('e2e')
const firstInstance = requests[1]

View File

@@ -1143,6 +1143,7 @@ describe('lib/cypress', () => {
setProxy: sinon.stub().resolves(),
setUserAgent: sinon.stub(),
on: sinon.stub(),
removeListener: sinon.stub(),
},
}

View File

@@ -476,12 +476,13 @@ describe('Server', () => {
.get('/c').reply(200, 'notHtml', { 'content-type': 'text/html;charset=utf-8' })
// invalid, but let's be tolerant
.get('/d').reply(200, 'notHtml', { 'content-type': 'text/html;' })
.get('/e').reply(200, 'notHtml', { 'content-type': 'application/xhtml+xml' })
const bad = await this.server._onResolveUrl('http://example.com/a', {}, this.automationRequest)
expect(bad.isHtml).to.be.false
for (const path of ['/b', '/c', '/d']) {
for (const path of ['/b', '/c', '/d', '/e']) {
const details = await this.server._onResolveUrl(`http://example.com${path}`, {}, this.automationRequest)
expect(details.isHtml).to.be.true

View File

@@ -416,7 +416,6 @@ const e2e = {
if (process.env.NO_EXIT) {
Fixtures.scaffoldWatch()
process.env.CYPRESS_INTERNAL_E2E_TESTS
}
sinon.stub(process, 'exit')

View File

@@ -54,7 +54,7 @@ const routeHandlers = {
postRun: {
method: 'post',
url: '/runs',
req: 'postRunRequest@2.3.0',
req: 'postRunRequest@2.4.0',
resSchema: 'postRunResponse@2.2.0',
res: (req, res) => {
if (!req.body.specs) {

View File

@@ -273,6 +273,7 @@ describe('lib/modes/record', () => {
version: '59',
}
const tag = 'nightly,develop'
const testingType = 'e2e'
return recordMode.createRunAndRecordSpecs({
key,
@@ -287,6 +288,7 @@ describe('lib/modes/record', () => {
specPattern,
runAllSpecs,
tag,
testingType,
})
.then(() => {
expect(commitInfo.commitInfo).to.be.calledWith(projectRoot)
@@ -297,6 +299,7 @@ describe('lib/modes/record', () => {
projectId,
ciBuildId,
recordKey: key,
testingType,
specPattern: 'spec/pattern1,spec/pattern2',
specs: ['path/to/spec/a', 'path/to/spec/b'],
platform: {

View File

@@ -6,9 +6,9 @@ import proxyquire from 'proxyquire'
const ERROR_MESSAGE = 'Setting the NODE_TLS_REJECT_UNAUTHORIZED'
const TLS_CONNECT = `require('tls').connect().on('error', ()=>{});`
const SUPPRESS_WARNING = `require('${__dirname}/../../lib/util/suppress_unauthorized_warning').suppress();`
const SUPPRESS_WARNING = `require('${__dirname}/../../lib/util/suppress_warnings').suppress();`
describe('lib/util/suppress_unauthorized_warning', function () {
describe('lib/util/suppress_warnings', function () {
it('tls.connect emits warning if NODE_TLS_REJECT_UNAUTHORIZED=0 and not suppressed', function () {
return execa.shell(`node -e "${TLS_CONNECT}"`, {
env: {
@@ -36,7 +36,7 @@ describe('lib/util/suppress_unauthorized_warning', function () {
const emitWarning = sinon.spy(process, 'emitWarning')
// force typescript to always be non-requireable
const { suppress } = proxyquire('../../lib/util/suppress_unauthorized_warning', {})
const { suppress } = proxyquire('../../lib/util/suppress_warnings', {})
suppress()

View File

@@ -10,12 +10,15 @@ describe('lib/updater', () => {
it('sends the right headers', () => {
sinon.stub(rp, 'get').resolves({})
Updater._getManifest('machine-id')
Updater._getManifest({ testingType: 'type', initialLaunch: true, id: 'machine-id' })
expect(rp.get).to.be.calledWithMatch({
headers: {
'x-cypress-version': pkg.version,
'x-os-name': 'linux',
'x-machine-id': 'machine-id',
'x-initial-launch:': 'true',
'x-testing-type': 'type',
},
})
})

View File

@@ -2244,10 +2244,10 @@
lodash.merge "^4.6.2"
lodash.omit "^4.5.0"
"@cypress/json-schemas@5.37.3":
version "5.37.3"
resolved "https://registry.yarnpkg.com/@cypress/json-schemas/-/json-schemas-5.37.3.tgz#2aed2fadc9533cb8d812ab27df77ea40ccf17759"
integrity sha512-vMhydN3Ysx+d/PnbOixPzlWdvaOeHmano61D2Cwshexo3F8L68xsXqF6zy3T6teNWHXTMtNZ6fQUpcy3r082XQ==
"@cypress/json-schemas@5.38.0":
version "5.38.0"
resolved "https://registry.yarnpkg.com/@cypress/json-schemas/-/json-schemas-5.38.0.tgz#8aad0c140eea4b0bab43f3f9e3df53386040db8f"
integrity sha512-VEyiQ48KrFXJXg7giK6+uTQXa6GPDlnQl1Ss9OYxzhl5feoiV6ioHUKZKvrO+Acu4S9vFlpl3Xvvwlz0azw6kA==
dependencies:
"@cypress/schema-tools" "4.7.7"
lodash "^4.17.21"