Merge branch 'develop'

This commit is contained in:
Zach Bloomquist
2021-04-07 12:49:16 -04:00
60 changed files with 328 additions and 264 deletions

View File

@@ -1102,15 +1102,6 @@ jobs:
path: /tmp/artifacts
- store-npm-logs
run-launcher:
<<: *defaults
steps:
- attach_workspace:
at: ~/
- run:
command: node index.js
working_directory: packages/launcher
npm-webpack-preprocessor:
<<: *defaults
steps:
@@ -1846,9 +1837,6 @@ linux-workflow: &linux-workflow
- ui-components-integration-tests:
requires:
- build
- run-launcher:
requires:
- build
- npm-webpack-dev-server:
requires:
@@ -1898,7 +1886,6 @@ linux-workflow: &linux-workflow
- npm-rollup-dev-server
# - npm-vite-dev-server
- npm-webpack-dev-server
- run-launcher
- ui-components-integration-tests
- reporter-integration-tests
- Linux lint

View File

@@ -37,7 +37,7 @@
"mock-fs": "4.13.0",
"shx": "0.3.3",
"snap-shot-it": "7.9.3",
"typescript": "4.0.3"
"typescript": "^4.2.3"
},
"bin": {
"create-cypress-tests": "dist/src/index.js"

View File

@@ -53,8 +53,12 @@
"rollup-plugin-postcss-modules": "2.0.2",
"rollup-plugin-typescript2": "^0.29.0",
"sass": "1.32.8",
"typescript": "4.0.3",
"vite": "2.1.3"
"sass-loader": "10.1.1",
"style-loader": "0.23.1",
"svg-url-loader": "3.0.3",
"typescript": "^4.2.3",
"vite": "2.1.3",
"webpack": "4.44.1"
},
"peerDependencies": {
"react": "^=16.x || ^=17.x",

View File

@@ -90,7 +90,7 @@
"style-loader": "0.23.1",
"styled-components": "5.0.0",
"svg-url-loader": "3.0.3",
"typescript": "4.0.3",
"typescript": "^4.2.3",
"victory": "34.3.6",
"webpack": "4.44.1",
"webpack-cli": "3.3.9"

View File

@@ -19,7 +19,7 @@
"@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-typescript": "~4.4.0",
"@vue/cli-service": "~4.4.0",
"typescript": "~3.9.3",
"typescript": "^4.2.3",
"vue-template-compiler": "^2.6.11"
}
}

View File

@@ -39,7 +39,8 @@
"rollup-plugin-istanbul": "2.0.1",
"rollup-plugin-typescript2": "^0.29.0",
"tailwindcss": "1.1.4",
"typescript": "3.9.6",
"tslib": "^2.1.0",
"typescript": "^4.2.3",
"unfetch": "4.1.0",
"vue": "2.6.12",
"vue-i18n": "8.9.0",

View File

@@ -40,7 +40,7 @@
"graphql": "14.0.0",
"mocha": "^8.1.1",
"react": "^16.13.1",
"typescript": "^3.9.7"
"typescript": "^4.2.3"
},
"peerDependencies": {
"@cypress/webpack-preprocessor": "^5.4.4"

View File

@@ -20,7 +20,7 @@
"chai": "^4.2.0",
"mocha": "^8.1.3",
"speed-measure-webpack-plugin": "1.4.2",
"typescript": "^3.9.7",
"typescript": "^4.2.3",
"webpack": "^4.44.2",
"webpack-dev-server": "^3.11.0"
},

View File

@@ -10,7 +10,7 @@
},
"devDependencies": {
"ts-loader": "7.0.4",
"typescript": "3.9.2"
"typescript": "^4.2.3"
},
"license": "ISC",
"author": "",

View File

@@ -1,6 +1,6 @@
{
"name": "cypress",
"version": "7.0.0",
"version": "7.0.1",
"description": "Cypress.io end to end testing tool",
"private": true,
"scripts": {
@@ -191,7 +191,7 @@
"terminal-banner": "1.1.0",
"through": "2.3.8",
"ts-node": "8.3.0",
"typescript": "3.7.4"
"typescript": "^4.2.3"
},
"engines": {
"node": ">=14.16.0",

View File

@@ -151,7 +151,7 @@ class SpecsList extends Component {
</Tooltip>
</div>
<div className='new-file-button'>
<button className='btn btn-primary' onClick={this._createNewFile.bind(this)}>New Spec File</button>
<button className='btn btn-link' onClick={this._createNewFile}><i className="fa fa-plus"></i> New Spec File</button>
</div>
</header>
{this._specsList()}
@@ -289,7 +289,7 @@ class SpecsList extends Component {
specsStore.toggleExpandSpecFolder(specFolderPath)
}
_createNewFile (e) {
_createNewFile = (e) => {
e.preventDefault()
e.stopPropagation()
@@ -405,7 +405,7 @@ class SpecsList extends Component {
<div className='empty-well'>
<h5>
No files found in
<code onClick={this._openIntegrationFolder.bind(this)}>
<code onClick={this._openIntegrationFolder}>
{this.props.project.integrationFolder}
</code>
</h5>
@@ -425,7 +425,7 @@ class SpecsList extends Component {
<div className="first-test-banner alert alert-info alert-dismissible">
<p>We've created some sample tests around key Cypress concepts. Run the first one or create your own test file.</p>
<p><a onClick={this._openHelp}>How to write tests</a></p>
<button className="close" onClick={this._removeFirstTestBanner.bind(this)}><span>&times;</span></button>
<button className="close" onClick={this._removeFirstTestBanner}><span>&times;</span></button>
</div>
)
}
@@ -440,7 +440,7 @@ class SpecsList extends Component {
)
}
_openIntegrationFolder () {
_openIntegrationFolder = () => {
ipc.openFinder(this.props.project.integrationFolder)
}
@@ -449,7 +449,7 @@ class SpecsList extends Component {
ipc.externalOpen('https://on.cypress.io/writing-first-test')
}
_removeFirstTestBanner () {
_removeFirstTestBanner = () => {
this.setState({ firstTestBannerDismissed: true })
}
}

View File

@@ -1,41 +0,0 @@
// @ts-check
// compile TypeScript files on the fly using
// Node require hook project
if (process.env.CYPRESS_INTERNAL_ENV !== 'production') {
require('@packages/ts/register')
}
const launcher = require('./lib/launcher')
module.exports = launcher
if (!module.parent) {
const Bluebird = require('bluebird')
// quick way to check if TS is working
/* eslint-disable no-console */
console.log('Launcher project exports')
console.log(launcher)
console.log('⛔️ please use it as a module, not from CLI')
if (process.argv.length > 2) {
const filenames = process.argv.slice(2)
Bluebird.each(filenames, (filename) => {
launcher.detectByPath(filename)
.then((foundBrowser) => {
console.log(` 👍 Found "${filename}":`, foundBrowser)
})
.catch((err) => {
console.log(` 👎 Couldn't find "${filename}:"`, err.message)
})
})
} else {
launcher.detect().then((browsers) => {
console.log('detected %s', browsers.length === 1 ? 'browser' : 'browsers')
console.log(browsers)
}, console.error)
}
/* eslint-enable no-console */
}

View File

@@ -0,0 +1,11 @@
import { detect, detectByPath } from './lib/detect'
import { launch } from './lib/browsers'
export {
detect,
detectByPath,
launch,
}
export * from './lib/types'

View File

@@ -1,8 +0,0 @@
import { launch } from './browsers'
import { detect, detectByPath } from './detect'
export { detect }
export { detectByPath }
export { launch }

View File

@@ -1,6 +1,8 @@
import { ChildProcess } from 'child_process'
import * as Bluebird from 'bluebird'
// TODO: Clean up this file
// TODO: some of these types can be combined with cli/types/index.d.ts
type BrowserName = 'electron' | 'chrome' | 'chromium' | 'firefox' | string

View File

@@ -28,12 +28,12 @@
"cross-env": "6.0.3",
"mocha": "3.5.3",
"shelljs": "0.8.3",
"sinon": "5.1.1",
"sinon": "^10.0.0",
"sinon-chai": "3.4.0",
"typescript": "3.5.3"
"typescript": "^4.2.3"
},
"files": [
"lib"
],
"types": "./lib/types.ts"
}
"types": "index.ts"
}

View File

@@ -1,13 +0,0 @@
require('../spec_helper')
import { expect } from 'chai'
const api = require('../..')
describe('launcher', () => {
it('has all needed methods', () => {
expect(api.launch).to.be.a('function')
expect(api.detect).to.be.a('function')
expect(api.detectByPath).to.be.a('function')
})
})

View File

@@ -1,8 +1,7 @@
{
"extends": "./../ts/tsconfig.json",
"include": [
"lib/*.ts",
"lib/**/*.ts"
"**/*.ts"
],
"files": [
"./../ts/index.d.ts"

View File

@@ -1,6 +1,6 @@
import Bluebird from 'bluebird'
import debugModule from 'debug'
import dns from 'dns'
import dns, { LookupAddress, LookupAllOptions } from 'dns'
import _ from 'lodash'
import net from 'net'
import tls from 'tls'
@@ -9,7 +9,7 @@ const debug = debugModule('cypress:network:connect')
export function byPortAndAddress (port: number, address: net.Address) {
// https://nodejs.org/api/net.html#net_net_connect_port_host_connectlistener
return new Bluebird((resolve, reject) => {
return new Bluebird<net.Address>((resolve, reject) => {
const onConnect = () => {
client.end()
resolve(address)
@@ -21,14 +21,14 @@ export function byPortAndAddress (port: number, address: net.Address) {
})
}
export function getAddress (port: number, hostname: string) {
export function getAddress (port: number, hostname: string): Bluebird<net.Address> {
debug('beginning getAddress %o', { hostname, port })
const fn = byPortAndAddress.bind({}, port)
// promisify at the very last second which enables us to
// modify dns lookup function (via hosts overrides)
const lookupAsync = Bluebird.promisify(dns.lookup, { context: dns })
const lookupAsync = Bluebird.promisify<LookupAddress[], string, LookupAllOptions>(dns.lookup, { context: dns })
// this does not go out to the network to figure
// out the addresess. in fact it respects the /etc/hosts file
@@ -36,7 +36,7 @@ export function getAddress (port: number, hostname: string) {
// https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback
// @ts-ignore
return lookupAsync(hostname, { all: true })
.then((addresses: net.Address[]) => {
.then((addresses) => {
debug('got addresses %o', { hostname, port, addresses })
// convert to an array if string

View File

@@ -46,7 +46,10 @@ export function removePort (urlObject) {
// set host to undefined else url.format(...) will ignore the port property
// https://nodejs.org/api/url.html#url_url_format_urlobject
// Additionally, the types are incorrect (don't include undefined), so we add TS exceptions
/* @ts-ignore */
delete parsed.host
/* @ts-ignore */
delete parsed.port
return parsed
@@ -68,10 +71,12 @@ export function addDefaultPort (urlToCheck) {
if (!parsed.port) {
// unset host...
// see above for reasoning
/* @ts-ignore */
delete parsed.host
if (parsed.protocol) {
parsed.port = DEFAULT_PROTOCOL_PORTS[parsed.protocol]
} else {
/* @ts-ignore */
delete parsed.port
}
}

View File

@@ -32,7 +32,7 @@
"mocha": "6.2.2",
"sinon": "7.3.1",
"sinon-chai": "3.3.0",
"typescript": "3.5.3"
"typescript": "^4.2.3"
},
"files": [
"lib"

View File

@@ -23,7 +23,7 @@ const stripPort = (url) => {
}
export class HttpBuffers {
buffer: Optional<HttpBuffer> = undefined
buffer: Optional<HttpBuffer> | undefined = undefined
reset (): void {
debug('resetting buffers')

View File

@@ -31,7 +31,7 @@
"@types/supertest": "2.0.10",
"express": "4.17.1",
"supertest": "6.0.1",
"typescript": "3.5.3"
"typescript": "^4.2.3"
},
"files": [
"lib"

View File

@@ -45,6 +45,7 @@ export const getPaths = (urlStr: string) => {
const sourceFileName = `${path.basename(parsed.path || '')} (original)`
parsed.pathname = path.dirname(parsed.pathname || '')
/* @ts-ignore */
delete parsed.search
return { sourceRoot: parsed.format(), sourceFileName, sourceMapName: `${sourceFileName}.map` }

View File

@@ -1,4 +1,5 @@
/// <reference types="@percy/cypress" />
/// <reference types="cypress-real-events" />
import React from 'react'
import { mount } from '@cypress/react'
import RunnerCt from '../../src/app/RunnerCt'
@@ -62,6 +63,20 @@ describe('RunnerCt', () => {
cy.percySnapshot()
})
it('shows hint message if no component specs', () => {
mount(
<RunnerCt
state={makeState({ specs: [] })}
// @ts-ignore - this is difficult to stub. Real one breaks things.
eventManager={new FakeEventManager()}
config={{ ...fakeConfig, projectRoot: '/root', componentFolder: '/root/src' }}
/>,
)
cy.contains('No specs found')
cy.percySnapshot()
})
context('keyboard shortcuts', () => {
it('toggles specs list drawer using shortcut', () => {
const saveState = cy.stub()

View File

@@ -1,7 +1,10 @@
import * as React from 'react'
import './NoSpecSelected.scss'
import './NoSpec.scss'
export const NoSpecSelected: React.FC = ({ children }) => {
export const NoSpec: React.FC<{ message?: React.ReactNode }> = ({
children,
message = 'No spec selected.',
}) => {
return (
<div className="no-spec">
<div className="no-spec-content-container">
@@ -9,7 +12,7 @@ export const NoSpecSelected: React.FC = ({ children }) => {
<path d="M33.3333 49H6.66659C5.2521 49 3.89554 48.4381 2.89535 47.4379C1.89515 46.4377 1.33325 45.0812 1.33325 43.6667V6.33333C1.33325 4.91885 1.89515 3.56229 2.89535 2.5621C3.89554 1.5619 5.2521 1 6.66659 1H21.5626C22.2698 1.00015 22.9479 1.2812 23.4479 1.78133L37.8853 16.2187C38.3854 16.7186 38.6664 17.3968 38.6666 18.104V43.6667C38.6666 45.0812 38.1047 46.4377 37.1045 47.4379C36.1043 48.4381 34.7477 49 33.3333 49Z" stroke="#B4B5BC" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<h2 className="no-spec-title">
{' No spec selected. '}
{message}
</h2>
{children && (

View File

@@ -20,7 +20,7 @@ export const Plugins = namedObserver('Plugins',
return (
<Hidden
type="visual"
type="layout"
hidden={!props.state.isAnyPluginToShow}
className={styles.ctPlugins}
>

View File

@@ -8,7 +8,7 @@ import EventManager from '../lib/event-manager'
import State from '../lib/state'
import { namedObserver } from '../lib/mobx'
import { ReporterHeader } from './ReporterHeader'
import { NoSpecSelected } from './NoSpecSelected'
import { NoSpec } from './NoSpec'
import styles from './RunnerCt.module.scss'
@@ -23,7 +23,7 @@ export const ReporterContainer = namedObserver('ReporterContainer',
if (!props.state.spec) {
return (
<div className='no-spec' data-cy="no-spec-selected-reporter">
<NoSpecSelected />
<NoSpec />
</div>
)
}

View File

@@ -129,4 +129,16 @@ $box-shadow-closest: 0px 0px 5px rgba(0, 0, 0, 0.4);
.largerIcon {
font-size: 1.75rem !important;
}
.noSpecsDescription {
text-align: center;
margin: 0 1rem;
line-height: 1.5;
.folder {
border-radius: 4px;
padding: 1px 4px;
background-color: #ddd;
}
}

View File

@@ -25,6 +25,7 @@ import { SpecList } from './SpecList/SpecList'
import { FileNode } from './SpecList/makeFileHierarchy'
import styles from './RunnerCt.module.scss'
import './RunnerCt.scss'
import { NoSpec } from './NoSpec'
interface RunnerCtProps {
state: State
@@ -199,16 +200,37 @@ const RunnerCt = namedObserver('RunnerCt',
onDragFinished={persistWidth('ctSpecListWidth')}
onChange={debounce(updateSpecListWidth)}
>
<SpecList
specs={props.state.specs}
selectedFile={state.spec ? state.spec.relative : undefined}
focusSpecList={focusSpecsList}
searchRef={searchRef}
className={cs(styles.specsList, {
'display-none': hideSpecsListIfNecessary(state),
})}
onFileClick={runSpec}
/>
{
state.specs.length < 1 ? (
<NoSpec message="No specs found">
<p className={styles.noSpecsDescription}>
Create a new spec file in
{' '}
<span className={styles.folder}>
{
props.config.componentFolder
? props.config.componentFolder.replace(props.config.projectRoot, '')
: 'the component specs folder'
}
</span>
{' '}
and it will immediately appear here.
</p>
</NoSpec>
) : (
<SpecList
specs={props.state.specs}
selectedFile={state.spec ? state.spec.relative : undefined}
focusSpecList={focusSpecsList}
searchRef={searchRef}
className={cs(styles.specsList, {
'display-none': hideSpecsListIfNecessary(state),
})}
onFileClick={runSpec}
/>
)
}
<SpecContent
state={props.state}
eventManager={props.eventManager}

View File

@@ -7,7 +7,7 @@ import Iframes from '../iframe/iframes'
import { debounce } from '../lib/debounce'
import { Message } from '../message/message'
import { KeyboardHelper } from './KeyboardHelper'
import { NoSpecSelected } from './NoSpecSelected'
import { NoSpec } from './NoSpec'
import { Plugins } from './Plugins'
import { ReporterContainer } from './ReporterContainer'
import { PLUGIN_BAR_HEIGHT } from './RunnerCt'
@@ -66,9 +66,9 @@ export const SpecContent = namedObserver('SpecContent', (props: SpecContentProps
{props.state.spec
? <Iframes {...props} />
: (
<NoSpecSelected>
<NoSpec>
<KeyboardHelper />
</NoSpecSelected>
</NoSpec>
)}
<Message state={props.state} />
</div>

View File

@@ -66,9 +66,13 @@
margin-inline-start: $text-xs;
}
.folderIcon, .brandIcon {
margin-right: 0.5em;
.li svg {
margin-right: 0.3em;
}
// TODO: fix this
// .folderIcon, .brandIcon {
// margin-right: 1em;
// }
.isClosed {
+ .ul, + .li {

View File

@@ -357,6 +357,10 @@ export const SpecList: React.FC<SpecListProps> = (props) => {
if (e.key === 'Enter') {
const selected = flattenedFiles[selectedSpecIndex]
if (!selected) {
return // enter key doesn't do anything if we couldn't find any specs
}
if (selected.type === 'file') {
// Run the spec.
props.onFileClick(selected)

View File

@@ -113,11 +113,6 @@ exports['e2e plugin run events / handles video being deleted in after:spec'] = `
1 passing
Warning: We could not find the video at the following path, so we were unable to process it.
Video path: /foo/bar/.projects/plugin-after-spec-deletes-video/cypress/videos/after_spec_deletes_video.js.mp4
This error will not alter the exit code.
(Results)

View File

@@ -63,6 +63,7 @@ export const _cookieMatches = (cookie: CyCookie, filter: CyCookieFilter) => {
export const CdpAutomation = (sendDebuggerCommandFn: SendDebuggerCommand) => {
const normalizeGetCookieProps = (cookie: cdp.Network.Cookie): CyCookie => {
if (cookie.expires === -1) {
// @ts-ignore
delete cookie.expires
}
@@ -71,6 +72,7 @@ export const CdpAutomation = (sendDebuggerCommandFn: SendDebuggerCommand) => {
// @ts-ignore
cookie.expirationDate = cookie.expires
// @ts-ignore
delete cookie.expires
// @ts-ignore

View File

@@ -7,6 +7,7 @@ import os from 'os'
import path from 'path'
import extension from '@packages/extension'
import mime from 'mime'
import { launch } from '@packages/launcher'
import appData from '../util/app_data'
import { fs } from '../util/fs'
@@ -493,7 +494,7 @@ export = {
// first allows us to connect the remote interface,
// start video recording and then
// we will load the actual page
const launchedBrowser = await utils.launch(browser, 'about:blank', args)
const launchedBrowser = await launch(browser, 'about:blank', args)
la(launchedBrowser, 'did not get launched browser instance')
@@ -517,6 +518,7 @@ export = {
// monkey-patch the .kill method to that the CDP connection is closed
const originalBrowserKill = launchedBrowser.kill
/* @ts-expect-error */
launchedBrowser.kill = async (...args) => {
debug('closing remote interface client')

View File

@@ -5,6 +5,7 @@ import Debug from 'debug'
import getPort from 'get-port'
import path from 'path'
import urlUtil from 'url'
import { launch } from '@packages/launcher/lib/browsers'
import FirefoxProfile from 'firefox-profile'
import firefoxUtil from './firefox-util'
import utils from './utils'
@@ -490,7 +491,7 @@ export async function open (browser: Browser, url, options: any = {}): Promise<B
debug('launch in firefox', { url, args: launchOptions.args })
const browserInstance = await utils.launch(browser, 'about:blank', launchOptions.args, {
const browserInstance = await launch(browser, 'about:blank', launchOptions.args, {
// sets headless resolution to 1920x1080 by default
// user can overwrite this default with these env vars or --height, --width arguments
MOZ_HEADLESS_WIDTH: '1920',

View File

@@ -151,8 +151,6 @@ module.exports = {
get: utils.getBrowsers,
launch: utils.launch,
close: kill,
_setInstance (_instance) {

View File

@@ -203,8 +203,6 @@ export = {
getBrowserByPath: launcher.detectByPath,
launch: launcher.launch,
writeExtension (browser, isTextTerminal, proxyUrl, socketIoRoute) {
debug('writing extension')

View File

@@ -60,6 +60,10 @@ try {
// https://github.com/electron/electron/issues/18214
app.commandLine.appendSwitch('disable-site-isolation-trials')
// prevent electron from using /dev/shm, which can cause crashes in Docker
// https://github.com/cypress-io/cypress/issues/15814
app.commandLine.appendSwitch('disable-dev-shm-usage')
if (os.platform() === 'linux') {
app.disableHardwareAcceleration()
}

View File

@@ -111,13 +111,6 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) {
This error will not alter the exit code.
${arg1}`
case 'VIDEO_DOESNT_EXIST':
return stripIndent`\
Warning: We could not find the video at the following path, so we were unable to process it.
Video path: ${arg1}
This error will not alter the exit code.`
case 'CHROME_WEB_SECURITY_NOT_SUPPORTED':
return stripIndent`\
Your project has set the configuration option: \`chromeWebSecurity: false\`

View File

@@ -1210,7 +1210,7 @@ module.exports = {
if (startedVideoCapture && !videoExists) {
// the video file no longer exists at the path where we expect it,
// likely because the user deleted it in the after:spec event
errors.warning('VIDEO_DOESNT_EXIST', videoName)
debug(`No video found after spec ran - skipping processing. Video path: ${videoName}`)
results.video = null
}

View File

@@ -11,7 +11,7 @@ const browserLaunch = require('./browser_launch')
const task = require('./task')
const util = require('../util')
const validateEvent = require('./validate_event')
const { registerTsNode } = require('../../util/ts-node')
const tsNodeUtil = require('./ts_node')
let registeredEventsById = {}
let registeredEventsByName = {}
@@ -164,7 +164,7 @@ const runPlugins = (ipc, pluginsFile, projectRoot) => {
})
if (!tsRegistered) {
registerTsNode(projectRoot, pluginsFile)
tsNodeUtil.register(projectRoot, pluginsFile)
// ensure typescript is only registered once
tsRegistered = true

View File

@@ -1,7 +1,7 @@
const debug = require('debug')('cypress:server:ts-node')
const path = require('path')
const tsnode = require('ts-node')
const resolve = require('./resolve')
const resolve = require('../../util/resolve')
const getTsNodeOptions = (tsPath, pluginsFile) => {
return {
@@ -16,7 +16,7 @@ const getTsNodeOptions = (tsPath, pluginsFile) => {
}
}
const registerTsNode = (projectRoot, pluginsFile) => {
const register = (projectRoot, pluginsFile) => {
try {
const tsPath = resolve.typescript(projectRoot)
@@ -36,5 +36,5 @@ const registerTsNode = (projectRoot, pluginsFile) => {
}
module.exports = {
registerTsNode,
register,
}

View File

@@ -6,6 +6,7 @@ const pkg = require('@packages/root')
const { agent } = require('@packages/network')
const konfig = require('./konfig')
const { machineId } = require('./util/machine_id')
const _getManifest = ({ id, initialLaunch, testingType }) => {
const url = konfig('desktop_manifest_url')
@@ -14,8 +15,9 @@ const _getManifest = ({ id, initialLaunch, testingType }) => {
headers: {
'x-cypress-version': pkg.version,
'x-os-name': os.platform(),
'x-arch': os.arch(),
'x-machine-id': id,
'x-initial-launch:': String(initialLaunch),
'x-initial-launch': String(initialLaunch),
'x-testing-type': testingType,
},
agent,

View File

@@ -5,20 +5,21 @@ const debug = Debug('cypress:server:lib:util:suppress_warnings')
let suppressed = false
/**
* Don't emit the NODE_TLS_REJECT_UNAUTHORIZED warning while
* we work on proper SSL verification.
* https://github.com/cypress-io/cypress/issues/5248
*/
const suppress = () => {
if (suppressed || process.env.CYPRESS_INTERNAL_ENV === 'production') {
// in development, still log warnings since they are helpful
if (suppressed) {
return
}
suppressed = true
const originalEmitWarning = process.emitWarning
process.emitWarning = (warning, type, code, ...args) => {
/**
* Don't emit the NODE_TLS_REJECT_UNAUTHORIZED warning while
* we work on proper SSL verification.
* https://github.com/cypress-io/cypress/issues/5248
*/
if (_.isString(warning) && _.includes(warning, 'NODE_TLS_REJECT_UNAUTHORIZED')) {
// https://github.com/nodejs/node/blob/85e6089c4db4da23dd88358fe0a12edefcd411f2/lib/internal/options.js#L17
@@ -33,7 +34,13 @@ const suppress = () => {
return
}
debug('suppressed emitWarning from node: %o', { process, warning, type, code, args })
if (process.env.CYPRESS_INTERNAL_ENV === 'production') {
debug('suppressed emitWarning from node: %o', { process, warning, type, code, args })
return
}
return originalEmitWarning.call(process, warning, type, code, ...args)
}
}

View File

@@ -199,10 +199,11 @@
"productName": "Cypress",
"workspaces": {
"nohoist": [
"@benmalka/foxdriver"
"@benmalka/foxdriver",
"tsconfig-paths"
]
},
"optionalDependencies": {
"registry-js": "1.13.0"
}
}
}

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/tsconfig-paths/lib/register.js b/node_modules/tsconfig-paths/lib/register.js
index c12b996..8beea8c 100644
--- a/node_modules/tsconfig-paths/lib/register.js
+++ b/node_modules/tsconfig-paths/lib/register.js
@@ -51,7 +51,7 @@ function register(explicitParams) {
explicitParams: explicitParams
});
if (configLoaderResult.resultType === "failed") {
- console.warn(configLoaderResult.message + ". tsconfig-paths will be skipped");
+ // console.warn(configLoaderResult.message + ". tsconfig-paths will be skipped");
return noOp;
}
var matchPath = match_path_sync_1.createMatchPath(configLoaderResult.absoluteBaseUrl, configLoaderResult.paths, configLoaderResult.mainFields, configLoaderResult.addMatchAll);

View File

@@ -15,6 +15,9 @@ describe('max listeners warning spec', () => {
browser: 'electron',
project: projectPath,
spec: '*',
processEnv: {
CYPRESS_INTERNAL_ENV: 'production',
},
onRun: async (exec) => {
const integrationPath = path.join(projectPath, 'cypress/integration')

View File

@@ -13,7 +13,8 @@ const snapshot = require('snap-shot-it')
const stripAnsi = require('strip-ansi')
const debug = require('debug')('test')
const pkg = require('@packages/root')
const launcher = require('@packages/launcher')
const detect = require('@packages/launcher/lib/detect')
const launch = require('@packages/launcher/lib/browsers')
const extension = require('@packages/extension')
const argsUtil = require(`${root}lib/util/args`)
const { fs } = require(`${root}lib/util/fs`)
@@ -125,7 +126,7 @@ describe('lib/cypress', () => {
sinon.stub(plugins, 'init').resolves(undefined)
sinon.stub(electronApp, 'isRunning').returns(true)
sinon.stub(extension, 'setHostAndPath').resolves()
sinon.stub(launcher, 'detect').resolves(TYPICAL_BROWSERS)
sinon.stub(detect, 'detect').resolves(TYPICAL_BROWSERS)
sinon.stub(process, 'exit')
sinon.stub(ServerE2E.prototype, 'reset')
sinon.stub(errors, 'warning')
@@ -1150,7 +1151,7 @@ describe('lib/cypress', () => {
ee.maximize = sinon.stub
ee.setSize = sinon.stub
sinon.stub(browserUtils, 'launch').resolves(ee)
sinon.stub(launch, 'launch').resolves(ee)
sinon.stub(Windows, 'create').returns(ee)
})
@@ -1181,7 +1182,7 @@ describe('lib/cypress', () => {
'--browser=chrome',
])
.then(() => {
const { args } = browserUtils.launch.firstCall
const { args } = launch.launch.firstCall
// when we work with the browsers we set a few extra flags
const chrome = _.find(TYPICAL_BROWSERS, { name: 'chrome' })

View File

@@ -3,6 +3,7 @@ require('../../spec_helper')
const os = require('os')
const extension = require('@packages/extension')
const launch = require('@packages/launcher/lib/browsers')
const plugins = require(`${root}../lib/plugins`)
const utils = require(`${root}../lib/browsers/utils`)
const chrome = require(`${root}../lib/browsers/chrome`)
@@ -44,7 +45,7 @@ describe('lib/browsers/chrome', () => {
sinon.stub(chrome, '_writeExtension').resolves('/path/to/ext')
sinon.stub(chrome, '_connectToChromeRemoteInterface').resolves(this.criClient)
sinon.stub(plugins, 'execute').callThrough()
sinon.stub(utils, 'launch').resolves(this.launchedBrowser)
sinon.stub(launch, 'launch').resolves(this.launchedBrowser)
sinon.stub(utils, 'getProfileDir').returns('/profile/dir')
sinon.stub(utils, 'ensureCleanCache').resolves('/profile/dir/CypressCache')
@@ -93,7 +94,7 @@ describe('lib/browsers/chrome', () => {
.then(() => {
// to initialize remote interface client and prepare for true tests
// we load the browser with blank page first
expect(utils.launch).to.be.calledWith('chrome', 'about:blank', args)
expect(launch.launch).to.be.calledWith('chrome', 'about:blank', args)
})
})
@@ -102,7 +103,7 @@ describe('lib/browsers/chrome', () => {
return chrome.open({ isHeadless: true, isHeaded: false }, 'http://', {}, this.automation)
.then(() => {
const args = utils.launch.firstCall.args[2]
const args = launch.launch.firstCall.args[2]
expect(args).to.include.members([
'--headless',
@@ -116,7 +117,7 @@ describe('lib/browsers/chrome', () => {
return chrome.open({ isHeadless: true, isHeaded: false }, 'http://', {}, this.automation)
.then(() => {
const args = utils.launch.firstCall.args[2]
const args = launch.launch.firstCall.args[2]
expect(args).to.include.members([
'--headless',
@@ -147,7 +148,7 @@ describe('lib/browsers/chrome', () => {
channel: 'stable',
}, 'http://', {}, this.automation)
.then(() => {
const args = utils.launch.firstCall.args[2]
const args = launch.launch.firstCall.args[2]
expect(args).to.include.members([
`--user-data-dir=${fullPath}`,
@@ -166,7 +167,7 @@ describe('lib/browsers/chrome', () => {
return chrome.open('chrome', 'http://', { onWarning }, this.automation)
.then(() => {
const args = utils.launch.firstCall.args[2]
const args = launch.launch.firstCall.args[2]
expect(args).to.deep.eq([
'--foo=bar',
@@ -190,7 +191,7 @@ describe('lib/browsers/chrome', () => {
return chrome.open('chrome', 'http://', {}, this.automation)
.then(() => {
const args = utils.launch.firstCall.args[2]
const args = launch.launch.firstCall.args[2]
expect(args).to.include.members([
'--foo=bar',
@@ -212,7 +213,7 @@ describe('lib/browsers/chrome', () => {
return chrome.open('chrome', 'http://', { onWarning }, this.automation)
.then(() => {
const args = utils.launch.firstCall.args[2]
const args = launch.launch.firstCall.args[2]
expect(args).to.include.members([
'--foo=bar',

View File

@@ -12,6 +12,7 @@ import firefoxUtil from '../../../lib/browsers/firefox-util'
const mockfs = require('mock-fs')
const FirefoxProfile = require('firefox-profile')
const launch = require('@packages/launcher/lib/browsers')
const utils = require('../../../lib/browsers/utils')
const plugins = require('../../../lib/plugins')
const protocol = require('../../../lib/browsers/protocol')
@@ -117,7 +118,7 @@ describe('lib/browsers/firefox', () => {
sinon.stub(plugins, 'has')
sinon.stub(plugins, 'execute')
sinon.stub(utils, 'writeExtension').resolves('/path/to/ext')
sinon.stub(utils, 'launch').resolves(this.browserInstance)
sinon.stub(launch, 'launch').resolves(this.browserInstance)
sinon.spy(FirefoxProfile.prototype, 'setPreference')
sinon.spy(FirefoxProfile.prototype, 'updatePreferences')
@@ -251,7 +252,7 @@ describe('lib/browsers/firefox', () => {
it('launches with the url and args', function () {
return firefox.open(this.browser, 'http://', this.options).then(() => {
expect(utils.launch).to.be.calledWith(this.browser, 'about:blank', [
expect(launch.launch).to.be.calledWith(this.browser, 'about:blank', [
'-marionette',
'-new-instance',
'-foreground',

View File

@@ -4,7 +4,7 @@ const info = require(`${root}../lib/modes/info`)
const capture = require(`${root}../lib/capture`)
const browserUtils = require(`${root}../lib/browsers/utils`)
const { fs } = require(`${root}../lib/util/fs`)
const launcher = require('@packages/launcher')
const detect = require('@packages/launcher/lib/detect')
const snapshot = require('snap-shot-it')
const stripAnsi = require('strip-ansi')
const _ = require('lodash')
@@ -48,17 +48,17 @@ describe('lib/modes/info', () => {
}
it('prints no browsers', async () => {
sinon.stub(launcher, 'detect').resolves([])
sinon.stub(detect, 'detect').resolves([])
await infoAndSnapshot('output without any browsers')
})
it('prints 1 found browser', async () => {
sinon.stub(launcher, 'detect').resolves([chromeStable])
sinon.stub(detect, 'detect').resolves([chromeStable])
await infoAndSnapshot('single chrome:stable')
})
it('prints 2 found browsers', async () => {
sinon.stub(launcher, 'detect').resolves([chromeStable, firefoxDev])
sinon.stub(detect, 'detect').resolves([chromeStable, firefoxDev])
// have to make sure random sampling from the browser list
// to create examples returns same order
// so Chrome will be picked first, Firefox will be second
@@ -72,7 +72,7 @@ describe('lib/modes/info', () => {
})
it('adds profile for browser if folder exists', async () => {
sinon.stub(launcher, 'detect').resolves([chromeStable, firefoxDev])
sinon.stub(detect, 'detect').resolves([chromeStable, firefoxDev])
sinon.stub(browserUtils, 'getBrowserPath')
.withArgs(chromeStable).returns('/path/to/user/chrome/profile')
.withArgs(firefoxDev).returns('/path/to/user/firefox/profile')

View File

@@ -590,18 +590,6 @@ describe('lib/modes/run', () => {
fs.pathExists.resolves(false)
})
it('logs warning', function () {
return runMode.waitForTestsToFinishRunning({
project: this.projectInstance,
startedVideoCapture: new Date(),
videoName: 'foo.mp4',
endVideoCapture: sinon.stub().resolves(),
})
.then(() => {
expect(errors.warning).to.be.calledWith('VIDEO_DOESNT_EXIST', 'foo.mp4')
})
})
it('does not process or upload video', function () {
return runMode.waitForTestsToFinishRunning({
project: this.projectInstance,

View File

@@ -2,16 +2,17 @@ require('../../../spec_helper')
const _ = require('lodash')
const snapshot = require('snap-shot-it')
const tsnode = require('ts-node')
const Promise = require('bluebird')
const preprocessor = require(`${root}../../lib/plugins/child/preprocessor`)
const task = require(`${root}../../lib/plugins/child/task`)
const runPlugins = require(`${root}../../lib/plugins/child/run_plugins`)
const util = require(`${root}../../lib/plugins/util`)
const resolve = require(`${root}../../lib/util/resolve`)
const browserUtils = require(`${root}../../lib/browsers/utils`)
const Fixtures = require(`${root}../../test/support/helpers/fixtures`)
const tsNodeUtil = require(`${root}../../lib/plugins/child/ts_node`)
const runPlugins = require(`${root}../../lib/plugins/child/run_plugins`)
const colorCodeRe = /\[[0-9;]+m/gm
const pathRe = /\/?([a-z0-9_-]+\/)*[a-z0-9_-]+\/([a-z_]+\.\w+)[:0-9]+/gmi
@@ -94,37 +95,25 @@ describe('lib/plugins/child/run_plugins', () => {
})
describe('typescript registration', () => {
beforeEach(function () {
this.register = sinon.stub(tsnode, 'register')
beforeEach(() => {
sinon.stub(tsNodeUtil, 'register')
sinon.stub(resolve, 'typescript').returns('/path/to/typescript.js')
})
it('registers ts-node if typescript is installed', function () {
it('registers ts-node', function () {
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
expect(this.register).to.be.calledWith({
transpileOnly: true,
compiler: '/path/to/typescript.js',
dir: '/path/to/plugins',
compilerOptions: {
module: 'CommonJS',
},
})
expect(tsNodeUtil.register).to.be.calledWith(
'proj-root',
'/path/to/plugins/file.js',
)
})
it('only registers ts-node once', function () {
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
expect(this.register).to.be.calledOnce
})
it('does not register ts-node if typescript is not installed', function () {
resolve.typescript.returns(null)
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
expect(this.register).not.to.be.called
expect(tsNodeUtil.register).to.be.calledOnce
})
})

View File

@@ -0,0 +1,44 @@
require('../../../spec_helper')
const tsnode = require('ts-node')
const resolve = require(`${root}../../lib/util/resolve`)
const tsNodeUtil = require(`${root}../../lib/plugins/child/ts_node`)
describe('lib/plugins/child/ts_node', () => {
beforeEach(() => {
sinon.stub(tsnode, 'register')
sinon.stub(resolve, 'typescript').returns('/path/to/typescript.js')
})
describe('typescript registration', () => {
it('registers ts-node if typescript is installed', () => {
tsNodeUtil.register('proj-root', '/path/to/plugins/file.js')
expect(tsnode.register).to.be.calledWith({
transpileOnly: true,
compiler: '/path/to/typescript.js',
dir: '/path/to/plugins',
compilerOptions: {
module: 'CommonJS',
},
})
})
it('does not register ts-node if typescript is not installed', () => {
resolve.typescript.returns(null)
tsNodeUtil.register('proj-root', '/path/to/plugins/file.js')
expect(tsnode.register).not.to.be.called
})
it('prevents tsconfig-paths from logging warning when there is no tsconfig.json', () => {
sinon.spy(console, 'warn')
tsNodeUtil.register('proj-root', '/path/to/plugins/file.js')
expect(console.warn).not.to.be.calledWith('Missing baseUrl in compilerOptions. tsconfig-paths will be skipped')
})
})
})

View File

@@ -1,30 +1,46 @@
require('../spec_helper')
const machineId = require(`${root}lib/util/machine_id`)
const os = require('os')
const rp = require('@cypress/request-promise')
const Updater = require(`${root}lib/updater`)
const pkg = require('@packages/root')
const machineId = require(`${root}lib/util/machine_id`)
const Updater = require(`${root}lib/updater`)
describe('lib/updater', () => {
context('_getManifest', () => {
context('._getManifest', () => {
const BASE_URL = 'https://download.cypress.io'
beforeEach(function () {
nock.cleanAll()
nock.enableNetConnect()
})
it('sends the right headers', () => {
sinon.stub(rp, 'get').resolves({})
sinon.stub(os, 'platform').returns('win32')
sinon.stub(os, 'arch').returns('x32')
Updater._getManifest({ testingType: 'type', initialLaunch: true, id: 'machine-id' })
nock(BASE_URL)
.matchHeader('x-cypress-version', pkg.version)
.matchHeader('x-os-name', 'win32')
.matchHeader('x-arch', 'x32')
.matchHeader('x-machine-id', 'machine-id')
.matchHeader('x-initial-launch', 'true')
.matchHeader('x-testing-type', 'type')
.get('/desktop.json')
.reply(200, {
version: '1000.0.0',
})
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',
},
return Updater
._getManifest({ testingType: 'type', initialLaunch: true, id: 'machine-id' })
.then((resp) => {
expect(resp.version).to.eq('1000.0.0')
})
})
})
context('check', () => {
context('.check', () => {
const version = pkg.version
beforeEach(() => {

View File

@@ -65,7 +65,7 @@ $font-sans: "Helvetica Neue", Helvetica, Arial, sans-serif !default;
[data-reach-dialog-overlay] {
display: flex;
padding: 2em 0;
z-index: 1;
z-index: 2;
}
[data-reach-dialog-content] {

View File

@@ -25043,7 +25043,7 @@ nise@^3.0.1:
lolex "^5.0.1"
path-to-regexp "^1.7.0"
nise@^4.0.1, nise@^4.0.4:
nise@^4.0.1, nise@^4.0.4, nise@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6"
integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==
@@ -30931,6 +30931,17 @@ sass-loader@10.1.0:
schema-utils "^3.0.0"
semver "^7.3.2"
sass-loader@10.1.1, sass-loader@^10.0.3:
version "10.1.1"
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d"
integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==
dependencies:
klona "^2.0.4"
loader-utils "^2.0.0"
neo-async "^2.6.2"
schema-utils "^3.0.0"
semver "^7.3.2"
sass-loader@7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.2.0.tgz#e34115239309d15b2527cb62b5dfefb62a96ff7f"
@@ -30953,17 +30964,6 @@ sass-loader@8.0.2:
schema-utils "^2.6.1"
semver "^6.3.0"
sass-loader@^10.0.3:
version "10.1.1"
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d"
integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==
dependencies:
klona "^2.0.4"
loader-utils "^2.0.0"
neo-async "^2.6.2"
schema-utils "^3.0.0"
semver "^7.3.2"
sass-lookup@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac"
@@ -31721,6 +31721,18 @@ sinon@9.0.2:
nise "^4.0.1"
supports-color "^7.1.0"
sinon@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-10.0.0.tgz#52279f97e35646ff73d23207d0307977c9b81430"
integrity sha512-XAn5DxtGVJBlBWYrcYKEhWCz7FLwZGdyvANRyK06419hyEpdT0dMc5A8Vcxg5SCGHc40CsqoKsc1bt1CbJPfNw==
dependencies:
"@sinonjs/commons" "^1.8.1"
"@sinonjs/fake-timers" "^6.0.1"
"@sinonjs/samsam" "^5.3.1"
diff "^4.0.2"
nise "^4.1.0"
supports-color "^7.1.0"
sinon@^9.0.0:
version "9.2.4"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b"
@@ -34231,7 +34243,7 @@ tslib@^1, tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0,
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3:
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
@@ -34413,36 +34425,21 @@ typescript-eslint-parser@^16.0.0:
lodash.unescape "4.0.1"
semver "5.5.0"
typescript@3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@3.7.4:
version "3.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19"
integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==
typescript@3.9.6:
version "3.9.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
typescript@4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5"
integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==
typescript@^2.5.1:
version "2.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
typescript@^3.0.3, typescript@^3.8.3, typescript@^3.9.7:
typescript@^3.0.3, typescript@^3.8.3:
version "3.9.9"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674"
integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==
typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
ua-parser-js@^0.7.18:
version "0.7.24"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"