mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-23 07:34:00 -05:00
Merge branch 'develop' into agg23/RerenderRecipe
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export const MIN_SUPPORTED_VERSION = {
|
||||
'react-scripts': '^=3.x || ^=4.x',
|
||||
next: '^=9.x',
|
||||
next: '^=9.x || ^=10.x',
|
||||
}
|
||||
|
||||
@@ -51,16 +51,22 @@ async function askForComponentTesting () {
|
||||
return shouldSetupComponentTesting
|
||||
}
|
||||
|
||||
function printCypressCommandsHelper ({ useYarn }: { useYarn: boolean }) {
|
||||
const displayedCommand = useYarn ? 'yarn' : 'npx'
|
||||
function printCypressCommandsHelper (options: { shouldSetupComponentTesting: boolean, useYarn: boolean }) {
|
||||
const printCommand = (command: string, description: string) => {
|
||||
const displayedRunner = options.useYarn ? 'yarn' : 'npx'
|
||||
|
||||
console.log('Inside this directory, you can run several commands:')
|
||||
console.log()
|
||||
console.log(chalk.cyan(` ${displayedCommand} cypress open`))
|
||||
console.log(' Opens cypress local development app.')
|
||||
console.log()
|
||||
console.log(chalk.cyan(` ${displayedCommand} cypress run`))
|
||||
console.log(' Runs tests in headless mode.')
|
||||
console.log()
|
||||
console.log(chalk.cyan(` ${displayedRunner} ${command}`))
|
||||
console.log(` ${description}`)
|
||||
}
|
||||
|
||||
printCommand('cypress open', 'Opens cypress local development app.')
|
||||
printCommand('cypress run', 'Runs tests in headless mode.')
|
||||
|
||||
if (options.shouldSetupComponentTesting) {
|
||||
printCommand('cypress open-ct', 'Opens cypress component-testing web app.')
|
||||
printCommand('cypress run', 'Runs component testing in headless mode.')
|
||||
}
|
||||
}
|
||||
|
||||
export async function main ({ useNpm, ignoreTs, setupComponentTesting, ignoreExamples }: MainArgv) {
|
||||
@@ -92,7 +98,7 @@ export async function main ({ useNpm, ignoreTs, setupComponentTesting, ignoreExa
|
||||
}
|
||||
|
||||
console.log(`\n👍 Success! Cypress is installed and ready to run tests.`)
|
||||
printCypressCommandsHelper({ useYarn })
|
||||
printCypressCommandsHelper({ useYarn, shouldSetupComponentTesting })
|
||||
|
||||
console.log(`\nHappy testing with ${chalk.green('cypress.io')} 🌲\n`)
|
||||
}
|
||||
|
||||
+7
-41
@@ -1,4 +1,4 @@
|
||||
> A little helper to unit test React components in the open source [Cypress.io](https://www.cypress.io/) E2E test runner **v4.5.0+**
|
||||
> A little helper to unit test React components in the open source [Cypress.io](https://www.cypress.io/) test runner **v7.0.0+**
|
||||
|
||||
**Jump to:** [Comparison](#comparison), [Blog posts](#blog-posts), [Install](#install), Examples: [basic](#basic-examples), [advanced](#advanced-examples), [full](#full-examples), [external](#external-examples), [Style options](#options), [Code coverage](#code-coverage), [Visual testing](#visual-testing), [Common problems](#common-problems), [Chat](#chat)
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||

|
||||
|
||||
- How is this different from [Enzyme](https://github.com/airbnb/enzyme) or [RTL](https://testing-library.com/docs/react-testing-library/intro)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/), and [visual testing](https://on.cypress.io/visual-testing). Ohh, and the code coverage is built-in!
|
||||
- How is this different from [Enzyme](https://github.com/airbnb/enzyme) or [RTL](https://testing-library.com/docs/react-testing-library/intro)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/), and [visual testing](https://on.cypress.io/visual-testing).
|
||||
- If you like using `@testing-library/react`, you can use `@testing-library/cypress` for the same `findBy`, `queryBy` commands, see one of the examples in the list below
|
||||
- Read [My Vision for Component Tests in Cypress](https://glebbahmutov.com/blog/my-vision-for-component-tests/)
|
||||
- Read [My Vision for Component Tests in Cypress](https://glebbahmutov.com/blog/my-vision-for-component-tests/) by Gleb Bahmutov
|
||||
|
||||
## Comparison
|
||||
|
||||
@@ -54,10 +54,10 @@ If you are coming from Enzyme world, check out the [enzyme](cypress/component/ba
|
||||
|
||||
## Install
|
||||
|
||||
Requires [Node](https://nodejs.org/en/) version 8 or above.
|
||||
Requires [Node](https://nodejs.org/en/) version 12 or above.
|
||||
|
||||
```sh
|
||||
npm install --save-dev cypress @cypress/react
|
||||
npm install --save-dev cypress @cypress/react @cypress/webpack-dev-server
|
||||
```
|
||||
|
||||
## Init
|
||||
@@ -371,44 +371,13 @@ Finally, when running tests on the continuous integration service, the true test
|
||||
|
||||
</details>
|
||||
|
||||
<details id="speed">
|
||||
<summary>Slow bundling</summary>
|
||||
|
||||
When you bundle spec file, you are now bundling React, Read DOM and other libraries, which is might be slow. For now, you can disable inline source maps by adding to your Webpack config settings (if available) the following:
|
||||
|
||||
```js
|
||||
const webpackOptions = {
|
||||
devtool: false,
|
||||
}
|
||||
```
|
||||
|
||||
Keep your eye on issue [#9663](https://github.com/cypress-io/cypress/issues/9663) for more information.
|
||||
|
||||
</details>
|
||||
|
||||
<details id="missing-code-coverage">
|
||||
<summary>Missing code coverage</summary>
|
||||
|
||||
If you are using your custom Webpack, this plugin might be missing code coverage information because the code was not instrumented. We try to insert the `babel-plugin-istanbul` plugin automatically, but your bundling might not use Babel, or configure it differently, preventing plugin insertion. Please let us know by opening an issue with full reproducible details.
|
||||
|
||||
See related issue [#141](https://github.com/bahmutov/cypress-react-unit-test/react/issues/141). You can also debug the plugin's behavior by running it with `DEBUG` environment variable, see [#debugging](#debugging) section.
|
||||
|
||||
</details>
|
||||
|
||||
<details id="gatsby-not-supported">
|
||||
<summary>Gatsby.js projects not supported</summary>
|
||||
|
||||
Currently, this project cannot find Webpack settings used by Gatsby.js, thus it cannot bundle specs and application code correctly. Keep an eye on [#307](https://github.com/cypress-io/cypress/issues/9671)
|
||||
|
||||
</details>
|
||||
|
||||
## Context Provider usage
|
||||
|
||||
React context provider usage and API described in [./docs/providers-and-composition.md](./docs/providers-and-composition.md)
|
||||
|
||||
## Chat
|
||||
|
||||
We have a chat workspace at [https://component-testing.slack.com/](https://component-testing.slack.com/), you are welcome to [join us](https://join.slack.com/t/component-testing/shared_invite/zt-h93lrgsl-8WzE8yNQlcZuZji_gA_mtg).
|
||||
Come chat with us [on discord](https://discord.gg/7ZHYhZSW) in the #component-testing channel.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -432,12 +401,9 @@ DEBUG=@cypress/react,find-webpack
|
||||
|
||||
Same feature for unit testing components from other frameworks using Cypress
|
||||
|
||||
- [cypress-vue-unit-test](https://github.com/bahmutov/cypress-vue-unit-test)
|
||||
- [@cypress/vue](https://github.com/cypress-io/cypress/tree/develop/npm/vue)
|
||||
- [cypress-cycle-unit-test](https://github.com/bahmutov/cypress-cycle-unit-test)
|
||||
- [cypress-svelte-unit-test](https://github.com/bahmutov/cypress-svelte-unit-test)
|
||||
- [cypress-angular-unit-test](https://github.com/bahmutov/cypress-angular-unit-test)
|
||||
- [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
|
||||
- [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
|
||||
|
||||
[renovate-badge]: https://img.shields.io/badge/renovate-app-blue.svg
|
||||
[renovate-app]: https://renovateapp.com/
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
/// <reference types="cypress" />
|
||||
import * as React from 'react'
|
||||
import RouterPage from '../../pages/router'
|
||||
import { createRouter } from 'next/router'
|
||||
import { RouterContext } from 'next/dist/next-server/lib/router-context'
|
||||
import { mount } from '@cypress/react'
|
||||
|
||||
@@ -22,6 +21,9 @@ describe('Component with router usage', () => {
|
||||
reload: cy.spy(),
|
||||
back: cy.spy(),
|
||||
prefetch: cy.spy(),
|
||||
isReady: true,
|
||||
isPreview: false,
|
||||
isLocaleDomain: false,
|
||||
beforePopState: cy.spy(),
|
||||
}
|
||||
|
||||
@@ -34,33 +36,6 @@ describe('Component with router usage', () => {
|
||||
cy.contains('Next.js route /testPath')
|
||||
})
|
||||
|
||||
it('renders the component that uses next.js with parsed query', () => {
|
||||
// alternatively you can use next's internal `createRouter` function to create a real instance of NextRouter
|
||||
const router = createRouter(
|
||||
'/testPath',
|
||||
{ param1: 'param1' },
|
||||
'/asTestPath',
|
||||
{
|
||||
subscription: cy.spy(),
|
||||
initialProps: {},
|
||||
App: cy.spy(),
|
||||
Component: cy.spy(),
|
||||
pageLoader: cy.spy(),
|
||||
initialStyleSheets: [],
|
||||
wrapApp: cy.spy(),
|
||||
isFallback: false,
|
||||
},
|
||||
)
|
||||
|
||||
mount(
|
||||
<RouterContext.Provider value={router}>
|
||||
<RouterPage />
|
||||
</RouterContext.Provider>,
|
||||
)
|
||||
|
||||
cy.contains('My query: {"param1":"param1"}')
|
||||
})
|
||||
|
||||
it('pushes the new route', () => {
|
||||
const router = {
|
||||
pathname: '/router',
|
||||
@@ -76,6 +51,9 @@ describe('Component with router usage', () => {
|
||||
reload: cy.spy(),
|
||||
back: cy.spy(),
|
||||
prefetch: cy.spy(),
|
||||
isReady: true,
|
||||
isPreview: false,
|
||||
isLocaleDomain: false,
|
||||
beforePopState: cy.spy(),
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
"devDependencies": {
|
||||
"@cypress/react": "file:../../dist",
|
||||
"@mdx-js/loader": "^1.6.16",
|
||||
"@next/mdx": "10.0.7",
|
||||
"@next/mdx": "10.1.2",
|
||||
"@zeit/next-sass": "^1.0.1",
|
||||
"check-code-coverage": "1.9.2",
|
||||
"cypress-circleci-reporter": "0.2.0",
|
||||
"next": "10.0.7",
|
||||
"webpack": "^4.44.2"
|
||||
"next": "10.1.2"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
+406
-1832
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 752 KiB After Width: | Height: | Size: 3.2 MiB |
@@ -69,7 +69,7 @@
|
||||
"lodash": "4.17.15",
|
||||
"mobx": "6.0.0",
|
||||
"mobx-react-lite": "3.0.0",
|
||||
"next": "^9.5.3",
|
||||
"next": "^10.1.2",
|
||||
"pretty": "2.0.0",
|
||||
"prop-types": "15.7.2",
|
||||
"radioactive-state": "1.3.4",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @ts-check
|
||||
/// <reference types="next" />
|
||||
const debug = require('debug')('@cypress/react')
|
||||
const loadConfig = require('next/dist/next-server/server/config').default
|
||||
const getNextJsBaseWebpackConfig = require('next/dist/build/webpack-config').default
|
||||
@@ -14,7 +15,7 @@ async function getNextWebpackConfig (config) {
|
||||
isServer: false,
|
||||
pagesDir: config.projectRoot,
|
||||
entrypoints: {},
|
||||
rewrites: [],
|
||||
rewrites: { fallback: [], afterFiles: [], beforeFiles: [] },
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# ⚡️ + 🌲 Cypress Component Testing w/ Vite
|
||||
# @cypress/vite-dev-server
|
||||
|
||||
> ⚡️ + 🌲 Cypress Component Testing w/ Vite
|
||||
|
||||
To install vite in you component testing environment,
|
||||
1. Install it `yarn add @cypress/vite-dev-server`
|
||||
@@ -13,3 +15,46 @@ module.exports = (on, config) => {
|
||||
return config
|
||||
}
|
||||
```
|
||||
|
||||
# @cypress/webpack-dev-server
|
||||
|
||||
> **Note** this package is not meant to be used outside of cypress component testing.
|
||||
|
||||
Install `@cypress/vue` or `@cypress/react` to get this package working properly
|
||||
|
||||
## Architecture
|
||||
|
||||
### Cypress server
|
||||
|
||||
- Every HTTP request goes to the cypress server which returns an html page. We call "TOP" because of its name in the dev tools
|
||||
This page
|
||||
- renders the list of spec files
|
||||
- And the timetraveling command log
|
||||
- Finally, it renders an AUT Iframe. this iframe calls a url that has 2 parts concatenated.
|
||||
- a prefix: `__cypress/iframes/`
|
||||
- the path to the current. For example: `src/components/button.spec.tsx`
|
||||
- In the cypress server, calls prefixed with `__cypress/iframes/...` will be passed to the dev-server as `__cypress/src/index.html`
|
||||
- Every call with the prefix `__cypress/src/` will be passed to the dev-server to deal as is, without changes.
|
||||
|
||||
### Dev-server
|
||||
|
||||
- Responds to every query with the prefix `__cypress/src/` (base path should be this prefix).
|
||||
- Responds to `__cypress/src/index.html` with an html page.
|
||||
This page
|
||||
- will contain an element `<div id="__cy_root"></div>`. Tis will be used by mount function to mount the app containing the components we want.
|
||||
- will load support files
|
||||
- will load the current spec from the url
|
||||
- will start the test when both files are done loading
|
||||
- The server re-runs the tests as soon as the current spec or any dependency is updated by calling an event `devServerEvents.emit('dev-server:compile:success')`
|
||||
|
||||
## Vite dev server
|
||||
|
||||
- Takes the users `vite.config` and adds base of `__cypress/src/` and a cypress vite plugin.
|
||||
- The cypress plugin takes care of
|
||||
- responding to the index.html query with an html page
|
||||
- restarting the tests when files are changed
|
||||
- The HTML page calls a script that loads support file and the specs using a native `import()` function
|
||||
- Then triggers the loaded tests
|
||||
|
||||
Vite is reponsible for compiling and bundling all the files. We use its error overlay to display any transpiling error.
|
||||
Omly runtime errors have to be handled through cypress
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"pluginsFile": "cypress/plugins.js",
|
||||
"video": false,
|
||||
"testFiles": "**/*.spec.*",
|
||||
"componentFolder": "cypress/components",
|
||||
"supportFile": "cypress/support/support.js"
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "4.3.2"
|
||||
"debug": "^4.3.2",
|
||||
"get-port": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/react": "0.0.0-development",
|
||||
@@ -27,7 +28,7 @@
|
||||
"vue": "3.0.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">= 2"
|
||||
"vite": ">= 2.1.3"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,23 +1,9 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { debug as debugFn } from 'debug'
|
||||
import { start as createDevServer } from './startServer'
|
||||
import { UserConfig } from 'vite'
|
||||
import { Server } from 'http'
|
||||
import { start as createDevServer, StartDevServer } from './startServer'
|
||||
const debug = debugFn('cypress:vite-dev-server:vite')
|
||||
|
||||
interface Options {
|
||||
specs: any[] // Cypress.Cypress['spec'][] // Why isn't this working? It works for webpack-dev-server
|
||||
config: Record<string, string>
|
||||
devServerEvents: EventEmitter
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface StartDevServer {
|
||||
/* this is the Cypress options object */
|
||||
options: Options
|
||||
/* support passing a path to the user's webpack config */
|
||||
viteConfig?: UserConfig // TODO: implement taking in the user's vite configuration. Right now we don't
|
||||
}
|
||||
export { StartDevServer }
|
||||
|
||||
export interface ResolvedDevServerConfig {
|
||||
port: number
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { resolve } from 'path'
|
||||
import { readFile } from 'fs'
|
||||
import { promisify } from 'util'
|
||||
@@ -24,7 +23,7 @@ export const makeCypressPlugin = (
|
||||
if (env) {
|
||||
return {
|
||||
define: {
|
||||
'import.meta.env.__cypress_supportPath': JSON.stringify(resolve(projectRoot, supportFilePath)),
|
||||
'import.meta.env.__cypress_supportPath': JSON.stringify(supportFilePath ? resolve(projectRoot, supportFilePath) : undefined),
|
||||
'import.meta.env.__cypress_originAutUrl': JSON.stringify('__cypress/iframes/'),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,47 +1,49 @@
|
||||
import Debug from 'debug'
|
||||
import { StartDevServer } from '.'
|
||||
import { createServer, ViteDevServer, InlineConfig } from 'vite'
|
||||
import { createServer, ViteDevServer, InlineConfig, UserConfig } from 'vite'
|
||||
import { dirname, resolve } from 'path'
|
||||
import getPort from 'get-port'
|
||||
import { makeCypressPlugin } from './makeCypressPlugin'
|
||||
import { EventEmitter } from 'events'
|
||||
|
||||
const debug = Debug('cypress:vite-dev-server:start')
|
||||
|
||||
// TODO: Pull in types for Options so we can infer these
|
||||
const serverConfig = (projectRoot: string, supportFilePath: string, devServerEvents: EventEmitter): InlineConfig => {
|
||||
return {
|
||||
root: resolve(__dirname, projectRoot),
|
||||
base: '/__cypress/src/',
|
||||
plugins: [makeCypressPlugin(projectRoot, supportFilePath, devServerEvents)],
|
||||
server: {
|
||||
port: 0,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
// Necessary to avoid a "prefixIdentifiers" issue from slots mounting
|
||||
// Could be resolved in test-utils
|
||||
'@vue/compiler-core': resolve(dirname(require.resolve('@vue/compiler-core')), 'dist', 'compiler-core.cjs.js'),
|
||||
},
|
||||
},
|
||||
}
|
||||
interface Options {
|
||||
specs: Cypress.Cypress['spec'][]
|
||||
config: Record<string, string>
|
||||
devServerEvents: EventEmitter
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const resolveServerConfig = ({ viteConfig, options }: StartDevServer) => {
|
||||
const defaultServerConfig = serverConfig(
|
||||
options.config.projectRoot,
|
||||
options.config.supportFile,
|
||||
options.devServerEvents,
|
||||
)
|
||||
export interface StartDevServer {
|
||||
/* this is the Cypress options object */
|
||||
options: Options
|
||||
/* support passing a path to the user's webpack config */
|
||||
viteConfig?: UserConfig // TODO: implement taking in the user's vite configuration. Right now we don't
|
||||
}
|
||||
|
||||
const requiredOptions = {
|
||||
base: defaultServerConfig.base,
|
||||
root: defaultServerConfig.root,
|
||||
const resolveServerConfig = async ({ viteConfig, options }: StartDevServer): Promise<InlineConfig> => {
|
||||
const { projectRoot, supportFile } = options.config
|
||||
|
||||
const requiredOptions: InlineConfig = {
|
||||
base: '/__cypress/src/',
|
||||
root: projectRoot,
|
||||
}
|
||||
|
||||
const finalConfig = { ...defaultServerConfig, ...viteConfig, ...requiredOptions }
|
||||
const finalConfig: InlineConfig = { ...viteConfig, ...requiredOptions }
|
||||
|
||||
finalConfig.plugins = [...(viteConfig.plugins || []), defaultServerConfig.plugins[0]]
|
||||
finalConfig.server.port = defaultServerConfig.server.port
|
||||
finalConfig.plugins = [...(viteConfig.plugins || []), makeCypressPlugin(projectRoot, supportFile, options.devServerEvents)]
|
||||
|
||||
// This alias is necessary to avoid a "prefixIdentifiers" issue from slots mounting
|
||||
// only cjs compiler-core accepts using prefixIdentifiers in slots which vue test utils use.
|
||||
// Could we resolve this usage in test-utils?
|
||||
finalConfig.resolve = finalConfig.resolve || {}
|
||||
finalConfig.resolve.alias = {
|
||||
...finalConfig.resolve.alias,
|
||||
'@vue/compiler-core': resolve(dirname(require.resolve('@vue/compiler-core')), 'dist', 'compiler-core.cjs.js'),
|
||||
},
|
||||
|
||||
finalConfig.server = finalConfig.server || {}
|
||||
|
||||
finalConfig.server.port = await getPort({ port: 3000, host: 'localhost' }),
|
||||
|
||||
debug(`the resolved server config is ${JSON.stringify(finalConfig, null, 2)}`)
|
||||
|
||||
@@ -55,7 +57,7 @@ export async function start (devServerOptions: StartDevServer): Promise<ViteDevS
|
||||
}
|
||||
|
||||
debug('starting vite dev server')
|
||||
const resolvedConfig = resolveServerConfig(devServerOptions)
|
||||
const resolvedConfig = await resolveServerConfig(devServerOptions)
|
||||
|
||||
return createServer(resolvedConfig)
|
||||
}
|
||||
|
||||
+58
-144
@@ -3,7 +3,6 @@
|
||||
[![NPM][npm-icon] ][npm-url]
|
||||
|
||||
[![semantic-release][semantic-image] ][semantic-url]
|
||||
[![renovate-app badge][renovate-badge]][renovate-app]
|
||||
|
||||
> Browser-based Component Testing for Vue.js with the Open-Source [Cypress.io](https://www.cypress.io/) Test Runner ✌️🌲
|
||||
>
|
||||
@@ -11,8 +10,6 @@
|
||||
|
||||
**Jump to:** [Comparison](#comparison), [Blog posts](#blog-posts), Examples: [basic](#basic-examples), [advanced](#advanced-examples), [full](#full-examples), [external](#external-examples), [Code coverage](#code-coverage), [Development](#development)
|
||||
|
||||
**🚧 Notice** We are in the middle of moving into the Cypress NPM org, so any references to `cypress-vue-unit-test` should be switched to `@cypress/vue`. Once complete, this repository will be archived.
|
||||
|
||||
### What is @cypress/vue?
|
||||
This package allows you to use the [Cypress](https://www.cypress.io/) test runner to mount and test your components within Cypress. It is built on top of the [Vue Test Utils](https://github.com/vuejs/vue-test-utils) package.
|
||||
|
||||
@@ -25,26 +22,63 @@ It uses [Vue Test Utils](https://github.com/vuejs/vue-test-utils) under the hood
|
||||
|
||||
## Installation
|
||||
|
||||
<p align="center">
|
||||
<img src="./docs/commands.gif" width="400px" alt="Terminal typing vue add cypress-experimental"/>
|
||||
</p>
|
||||
- Requires Cypress v7.0.0 or later
|
||||
- Requires [Node](https://nodejs.org/en/) version 12 or above
|
||||
- Supports webpack-based projects, vite in alpha, if you would like us to support another, please [create an issue](https://github.com/cypress-io/cypress/issues/new?assignees=&labels=npm:%20@cypress/vue&template=3-feature.md) or, if an issue already exists subscribe to it.
|
||||
|
||||
- Requires Cypress v4.5.0 or later
|
||||
- Requires [Node](https://nodejs.org/en/) version 8 or above
|
||||
- Only supporting webpack-based projects
|
||||
- Installation via Vue CLI recommended, read [Write Your First Vue Component Test](https://glebbahmutov.com/blog/first-vue-component-test/)
|
||||
Now you are ready to install.
|
||||
|
||||
### Vue CLI Installation
|
||||
### Manual Installation
|
||||
|
||||
> Vue CLI v3+
|
||||
Using [@cypress/webpack-dev-server](https://github.com/cypress-io/cypress-webpack-preprocessor#readme) and [vue-loader](https://github.com/vuejs/vue-loader).
|
||||
|
||||
_Recommended_: One step install to existing projects with Vue CLI via [experimental plugin](https://github.com/jessicasachs/vue-cli-plugin-cypress-experimental), read [Write Your First Vue Component Test](https://glebbahmutov.com/blog/first-vue-component-test/)
|
||||
```js
|
||||
// cypress/plugins/index.js
|
||||
const webpack = require('@cypress/webpack-dev-server')
|
||||
const webpackOptions = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
```sh
|
||||
vue add cypress-experimental
|
||||
const options = {
|
||||
// send in the options from your webpack.config.js, so it works the same
|
||||
// as your app's code
|
||||
webpackOptions,
|
||||
watchOptions: {},
|
||||
}
|
||||
|
||||
module.exports = (on) => {
|
||||
on('dev-server:start', webpack(options))
|
||||
}
|
||||
```
|
||||
|
||||
If you want to install this package manually, follow [manual install](./docs/manual-install.md)
|
||||
Install dev dependencies
|
||||
|
||||
```shell
|
||||
npm i -D @cypress/webpack-dev-server \
|
||||
vue-loader vue-template-compiler css-loader
|
||||
```
|
||||
|
||||
And write a test
|
||||
|
||||
```js
|
||||
import Hello from '../../components/Hello.vue'
|
||||
import { mountCallback } from '@cypress/vue'
|
||||
|
||||
describe('Hello.vue', () => {
|
||||
beforeEach(mountCallback(Hello))
|
||||
|
||||
it('shows hello', () => {
|
||||
cy.contains('Hello World!')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Usage and Examples
|
||||
|
||||
@@ -82,8 +116,6 @@ mount(Todo, {
|
||||
|
||||
See examples below for details.
|
||||
|
||||
<a name="global-vue-extensions"/>
|
||||
|
||||
### Global Vue Options
|
||||
|
||||
You can pass extensions (global components, mixins, modules to use)
|
||||
@@ -95,9 +127,7 @@ the `options`.
|
||||
- `mixin` (alias `mixins`) - list of global mixins, see [Mixins](cypress/component/basic/mixins) example
|
||||
- `filters` - hash of global filters, see [Filters](cypress/component/basic/filters) example
|
||||
|
||||
<a name="intro-example"/>
|
||||
|
||||
### The intro example
|
||||
### intro example
|
||||
|
||||
Take a look at the first Vue v2 example:
|
||||
[Declarative Rendering](https://vuejs.org/v2/guide/#Declarative-Rendering).
|
||||
@@ -164,9 +194,7 @@ the reference `Cypress.vue.$data` and via GUI. The full power of the
|
||||
|
||||

|
||||
|
||||
<a name="list-example"/>
|
||||
|
||||
### The list example
|
||||
### list example
|
||||
|
||||
There is a list example next in the Vue docs.
|
||||
|
||||
@@ -233,8 +261,6 @@ describe('Declarative rendering', () => {
|
||||
|
||||

|
||||
|
||||
<a name="handling-user-input"/>
|
||||
|
||||
### Handling User Input
|
||||
|
||||
The next section in the Vue docs starts with [reverse message example](https://vuejs.org/v2/guide/#Handling-User-Input).
|
||||
@@ -358,8 +384,6 @@ describe('Several components', () => {
|
||||
})
|
||||
```
|
||||
|
||||
<a name="spying-example"/>
|
||||
|
||||
### Spying example
|
||||
|
||||
Button counter component is used in several Vue doc examples
|
||||
@@ -553,91 +577,7 @@ Repo | Description
|
||||
[vue-component-test-example](https://github.com/bahmutov/vue-component-test-example) | Scaffolded Vue CLI v3 project with added component tests, read [Write Your First Vue Component Test](https://glebbahmutov.com/blog/first-vue-component-test/).
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## Known problems
|
||||
|
||||
<a name="bundling"/>
|
||||
|
||||
## Bundling
|
||||
|
||||
How do we load this Vue file into the testing code? Using webpack preprocessor. Note that this module ships with [@cypress/webpack-preprocessor](https://github.com/cypress-io/cypress-webpack-preprocessor#compatibility) 2.x that requires Webpack 4.x. If you have Webpack 3.x please add `@cypress/webpack-preprocessor v1.x`.
|
||||
|
||||
<a name="short-way"/>
|
||||
|
||||
### Short way
|
||||
|
||||
#### For Webpack Users
|
||||
|
||||
Your project probably already has `webpack.config.js` setup to transpile
|
||||
`.vue` files. To load these files in the Cypress tests, grab the webpack
|
||||
processor included in this module, and load it from the `cypress/plugins/index.js`
|
||||
file.
|
||||
|
||||
```js
|
||||
const {
|
||||
onFilePreprocessor,
|
||||
} = require('@cypress/vue/preprocessor/webpack')
|
||||
module.exports = (on) => {
|
||||
on('file:preprocessor', onFilePreprocessor('../path/to/webpack.config'))
|
||||
}
|
||||
```
|
||||
|
||||
Cypress should be able to import `.vue` files in the tests
|
||||
|
||||
<a name="manual"/>
|
||||
|
||||
### Manual
|
||||
|
||||
Using [@cypress/webpack-preprocessor](https://github.com/cypress-io/cypress-webpack-preprocessor#readme) and [vue-loader](https://github.com/vuejs/vue-loader).
|
||||
You can use [cypress/plugins/index.js](cypress/plugins/index.js) to load `.vue` files
|
||||
using `vue-loader`.
|
||||
|
||||
```js
|
||||
// cypress/plugins/index.js
|
||||
const webpack = require('@cypress/webpack-preprocessor')
|
||||
const webpackOptions = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const options = {
|
||||
// send in the options from your webpack.config.js, so it works the same
|
||||
// as your app's code
|
||||
webpackOptions,
|
||||
watchOptions: {},
|
||||
}
|
||||
|
||||
module.exports = (on) => {
|
||||
on('file:preprocessor', webpack(options))
|
||||
}
|
||||
```
|
||||
|
||||
Install dev dependencies
|
||||
|
||||
```shell
|
||||
npm i -D @cypress/webpack-preprocessor \
|
||||
vue-loader vue-template-compiler css-loader
|
||||
```
|
||||
|
||||
And write a test
|
||||
|
||||
```js
|
||||
import Hello from '../../components/Hello.vue'
|
||||
import { mountCallback } from '@cypress/vue'
|
||||
|
||||
describe('Hello.vue', () => {
|
||||
beforeEach(mountCallback(Hello))
|
||||
|
||||
it('shows hello', () => {
|
||||
cy.contains('Hello World!')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Code coverage
|
||||
|
||||
@@ -659,27 +599,22 @@ If you want to disable code coverage instrumentation and reporting, use `--env c
|
||||
|
||||
We were in the middle of moving into the Cypress NPM org, so any references to `cypress-vue-unit-test` should be switched to `@cypress/vue`. Once complete, the old repository will be archived.
|
||||
|
||||
<a name="#development"/>
|
||||
|
||||
## Development
|
||||
|
||||
To see all local tests, install dependencies, build the code and open Cypress in GUI mode
|
||||
To see all local tests, install dependencies, build the code and open Cypress using the open-ct command
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run build
|
||||
yarn install
|
||||
yarn workspace @cypress/vue build
|
||||
```
|
||||
|
||||
The build is done using `tsc` that transpiles all files from [src](src) to the `dist` folder. You can then run component tests by opening Cypress
|
||||
The build is done using `rollup`. It bundles all files from [src](src) to the `dist` folder. You can then run component tests by opening Cypress
|
||||
|
||||
```sh
|
||||
npm run cy:open
|
||||
# cypress open-ct
|
||||
yarn workspace @cypress/vue cy:open
|
||||
```
|
||||
|
||||
and clicking on any component spec
|
||||
|
||||

|
||||
|
||||
Larger tests that use full application and run on CI (see [circle.yml](circle.yml)) are located in the folder [examples](examples).
|
||||
|
||||
### Debugging
|
||||
@@ -696,8 +631,6 @@ If some deeply nested objects are abbreviated and do not print fully, set the ma
|
||||
DEBUG=@cypress/vue DEBUG_DEPTH=10
|
||||
```
|
||||
|
||||
<a name="#related"/>
|
||||
|
||||
## Related info
|
||||
|
||||
- [Testing Vue web applications with Vuex data store & REST backend](https://www.cypress.io/blog/2017/11/28/testing-vue-web-application-with-vuex-data-store-and-rest-backend/)
|
||||
@@ -706,27 +639,10 @@ DEBUG=@cypress/vue DEBUG_DEPTH=10
|
||||
- [Learn TDD in Vue](https://learntdd.in/vue)
|
||||
- [@cypress/vue vs vue-test-utils](https://codingitwrong.com/2018/03/04/comparing-vue-component-testing-tools.html)
|
||||
|
||||
```js
|
||||
const {
|
||||
onFileDefaultPreprocessor,
|
||||
} = require('@cypress/vue/preprocessor/webpack')
|
||||
|
||||
module.exports = (on, config) => {
|
||||
require('@cypress/code-coverage/task')(on, config)
|
||||
on('file:preprocessor', onFileDefaultPreprocessor(config))
|
||||
|
||||
// IMPORTANT to return the config object
|
||||
// with the any changed environment variables
|
||||
return config
|
||||
}
|
||||
```
|
||||
|
||||
## Blog posts
|
||||
|
||||
- [Write Your First Vue Component Test](https://glebbahmutov.com/blog/first-vue-component-test/)
|
||||
|
||||
<a name="#other"/>
|
||||
|
||||
## Test adapters for other frameworks
|
||||
|
||||
- [@cypress/react](https://github.com/cypress-io/@cypress/react)
|
||||
@@ -736,8 +652,6 @@ module.exports = (on, config) => {
|
||||
- [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
|
||||
- [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
|
||||
|
||||
<a name="#maintainers"/>
|
||||
|
||||
## Maintainers
|
||||
|
||||
The Cypress.io Component Testing Team
|
||||
|
||||
@@ -1,17 +1,62 @@
|
||||
# Webpack-ct
|
||||
# @cypress/webpack-dev-server
|
||||
|
||||
> **Note** this package is not meant to be used outside of cypress component testing.
|
||||
|
||||
Install `@cypress/vue` or `@cypress/react` to get this package working properly
|
||||
|
||||
## Responsibilities
|
||||
To install `@cypress/webpack-dev-server` in you component testing environment,
|
||||
1. Install it `yarn add @cypress/webpack-dev-server`
|
||||
2. Add it to `cypress/plugins/index.js`
|
||||
|
||||
- Make a `webpack.config` from the users setup
|
||||
- add current project rules and aliases
|
||||
- remove eslint?
|
||||
- Launch webpack dev server
|
||||
- Update entry point (in `src/browser.ts`)
|
||||
- The entry point (`browser.ts`) has to delegate the loading of spec files to the loader + plugin
|
||||
```js
|
||||
import { startDevServer } from '@cypress/webpack-dev-server'
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('dev-server:start', async (options) => startDevServer({ options }))
|
||||
|
||||
return config
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Cypress server
|
||||
|
||||
- Every HTTP request goes to the cypress server which returns an html page. We call "TOP" because of its name in the dev tools
|
||||
This page
|
||||
- renders the list of spec files
|
||||
- And the timetraveling command log
|
||||
- Finally, it renders an AUT Iframe. this iframe calls a url that has 2 parts concatenated.
|
||||
- a prefix: `__cypress/iframes/`
|
||||
- the path to the current. For example: `src/components/button.spec.tsx`
|
||||
- In the cypress server, calls prefixed with `__cypress/iframes/...` will be passed to the dev-server as `__cypress/src/index.html`
|
||||
- Every call with the prefix `__cypress/src/` will be passed to the dev-server to deal as is, without changes.
|
||||
|
||||
### Dev-server
|
||||
|
||||
- Responds to every query with the prefix `__cypress/src/` (base path should be this prefix).
|
||||
- Responds to `__cypress/src/index.html` with an html page.
|
||||
This page
|
||||
- will contain an element `<div id="__cy_root"></div>` to attach the mounted components to.
|
||||
- will load support files
|
||||
- will load the current spec from the url
|
||||
- will start the test when both files are done loading
|
||||
- The server re-runs the tests as soon as the current spec or any dependency is updated
|
||||
by calling an event `devServerEvents.emit('dev-server:compile:success')`
|
||||
|
||||
## Webpack
|
||||
|
||||
Webpack-dev-server fulfills his reponsibilities by
|
||||
|
||||
- Making a `webpack.config` from the users setup by changing the entrypoint to `browser.ts`
|
||||
- Launches the webpack dev server with `devServer.publicPath = "__cypress/src/"` and `devServer.hot = false`
|
||||
- The entry point (`browser.ts`) delegates the loading of spec files to the loader.
|
||||
- By using a require on itself, it enables using a custom loader and Hot Module Replacements if hot is false.
|
||||
- The plugin is responsible for passing down the path to the support files and the list of specs discovered by the cypress server to the loader
|
||||
- The loader:
|
||||
- compares the url with each spec path given by the plugin
|
||||
- picks the spec that should be run
|
||||
- runs the `AUT-Runner.ts` to load and launch the support file and the current spec
|
||||
|
||||
## Performance tests
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
const { validatePeerDependencies } = require('./dist/errors')
|
||||
|
||||
validatePeerDependencies()
|
||||
|
||||
module.exports = require('./dist')
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@cypress/webpack-dev-server",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Launches Webpack Dev Server for Component Testing",
|
||||
"main": "index.js",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build-prod": "tsc",
|
||||
@@ -25,9 +25,9 @@
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"html-webpack-plugin": "> 3",
|
||||
"webpack": "> 4",
|
||||
"webpack-dev-server": "> 3"
|
||||
"html-webpack-plugin": ">=4",
|
||||
"webpack": ">=4",
|
||||
"webpack-dev-server": ">=4"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
export interface MissingDependency {
|
||||
prettyName: string
|
||||
packageName: string
|
||||
}
|
||||
|
||||
const peerDeps: MissingDependency[] = [
|
||||
{
|
||||
prettyName: 'Webpack Dev Server',
|
||||
packageName: 'webpack-dev-server',
|
||||
},
|
||||
{
|
||||
prettyName: 'Html Webpack Plugin',
|
||||
packageName: 'html-webpack-plugin',
|
||||
},
|
||||
{
|
||||
prettyName: 'Webpack',
|
||||
packageName: 'webpack',
|
||||
},
|
||||
]
|
||||
|
||||
const makePrettyLibs = (libs) => {
|
||||
return libs.reduce((acc, curr, idx) => {
|
||||
acc.prettyNames.push(curr.prettyName)
|
||||
acc.packageNames.push(curr.packageName)
|
||||
|
||||
if (idx >= libs.length) return acc
|
||||
|
||||
return {
|
||||
prettyNames: acc.prettyNames.join(', '),
|
||||
packageNames: acc.packageNames.join(' '),
|
||||
}
|
||||
}, { prettyNames: [], packageNames: [] })
|
||||
}
|
||||
|
||||
export class MissingPeerDependency extends Error {
|
||||
private libs: { prettyNames: string[], packageNames: string[] }
|
||||
|
||||
constructor (message,
|
||||
libs: MissingDependency[]) {
|
||||
const prettyLibs = makePrettyLibs(libs)
|
||||
|
||||
super(`${message} ${prettyLibs.prettyNames}`)
|
||||
this.name = 'PeerDependencyMissing'
|
||||
this.libs = prettyLibs
|
||||
|
||||
Object.setPrototypeOf(this, MissingPeerDependency.prototype)
|
||||
}
|
||||
|
||||
get prettyMessage () {
|
||||
return `❌ Missing ${this.libs.prettyNames}. Please install them with npm or yarn.
|
||||
|
||||
npm i ${this.libs.packageNames} -D
|
||||
yarn add ${this.libs.packageNames} --dev
|
||||
|
||||
Updating webpack config is unnecessary
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
export function validatePeerDependencies () {
|
||||
const missingPeerDeps = peerDeps.filter((peerDep) => {
|
||||
try {
|
||||
require(peerDep.packageName)
|
||||
} catch (err) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (missingPeerDeps.length) {
|
||||
const error = new MissingPeerDependency(`@cypress/webpack-dev-server is missing peer dependencies`, missingPeerDeps)
|
||||
|
||||
console.error(error.prettyMessage) // eslint-disable-line
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { debug as debugFn } from 'debug'
|
||||
import { AddressInfo } from 'net'
|
||||
import { start as createDevServer } from './startServer'
|
||||
import { UserWebpackDevServerOptions } from './makeWebpackConfig'
|
||||
import { start as createDevServer, StartDevServer } from './startServer'
|
||||
|
||||
const debug = debugFn('cypress:webpack-dev-server:webpack')
|
||||
|
||||
@@ -12,12 +11,7 @@ export interface ResolvedDevServerConfig {
|
||||
close: (done?: DoneCallback) => void
|
||||
}
|
||||
|
||||
export interface StartDevServer extends UserWebpackDevServerOptions {
|
||||
/* this is the Cypress options object */
|
||||
options: Cypress.DevServerOptions
|
||||
/* support passing a path to the user's webpack config */
|
||||
webpackConfig?: Record<string, any>
|
||||
}
|
||||
export { StartDevServer }
|
||||
|
||||
export async function startDevServer (startDevServerArgs: StartDevServer, exitProcess = process.exit) {
|
||||
const webpackDevServer = await createDevServer(startDevServerArgs, exitProcess)
|
||||
|
||||
@@ -22,7 +22,7 @@ interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions, UserWe
|
||||
isOpenMode: boolean
|
||||
}
|
||||
|
||||
const mergePublicPath = (baseValue: string, userValue = '/') => {
|
||||
const mergePublicPath = (baseValue = '/', userValue = '/') => {
|
||||
return path.join(baseValue, userValue, '/')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import Debug from 'debug'
|
||||
import webpack from 'webpack'
|
||||
import WebpackDevServer from 'webpack-dev-server'
|
||||
import { StartDevServer } from '.'
|
||||
import { makeWebpackConfig } from './makeWebpackConfig'
|
||||
import { makeWebpackConfig, UserWebpackDevServerOptions } from './makeWebpackConfig'
|
||||
|
||||
export interface StartDevServer extends UserWebpackDevServerOptions {
|
||||
/* this is the Cypress options object */
|
||||
options: Cypress.DevServerOptions
|
||||
/* support passing a path to the user's webpack config */
|
||||
webpackConfig?: Record<string, any>
|
||||
}
|
||||
|
||||
const debug = Debug('cypress:webpack-dev-server:start')
|
||||
|
||||
@@ -40,19 +46,6 @@ export async function start ({ webpackConfig: userWebpackConfig, options, ...use
|
||||
|
||||
debug('starting webpack dev server')
|
||||
|
||||
// TODO: write a test for how we are NOT modifying publicPath
|
||||
// here, and instead stripping it out of the cypress proxy layer
|
||||
//
|
||||
// ...this prevents a problem if users have a 'before' or 'after'
|
||||
// function defined in their webpack config, it does NOT
|
||||
// interfere with their routes... otherwise the public
|
||||
// path we are prefixing like /__cypress/src/ would be
|
||||
// prepended to req.url and cause their routing handlers to fail
|
||||
//
|
||||
// NOTE: we are merging in webpackConfig.devServer here so
|
||||
// that user values for the devServer get passed on correctly
|
||||
// since we are passing in the compiler directly, and these
|
||||
// devServer options would otherwise get ignored
|
||||
const webpackDevServerConfig = {
|
||||
...userWebpackConfig.devServer,
|
||||
hot: false,
|
||||
|
||||
@@ -14,6 +14,6 @@ module.exports = {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
plugins: [new HtmlWebpackPlugin({
|
||||
template: path.join(__dirname, '../index-template.html'),
|
||||
template: path.resolve(__dirname, '..', 'index-template.html'),
|
||||
})],
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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>
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -97,6 +97,7 @@ export function launch (
|
||||
browser: FoundBrowser,
|
||||
url: string,
|
||||
args: string[] = [],
|
||||
defaultBrowserEnv = {},
|
||||
) {
|
||||
log('launching browser %o', { browser, url })
|
||||
|
||||
@@ -110,7 +111,11 @@ export function launch (
|
||||
|
||||
log('spawning browser with args %o', { args })
|
||||
|
||||
const proc = cp.spawn(browser.path, args, { stdio: ['ignore', 'pipe', 'pipe'] })
|
||||
// allow setting default env vars such as MOZ_HEADLESS_WIDTH
|
||||
// but only if it's not already set by the environment
|
||||
const env = Object.assign({}, defaultBrowserEnv, process.env)
|
||||
|
||||
const proc = cp.spawn(browser.path, args, { stdio: ['ignore', 'pipe', 'pipe'], env })
|
||||
|
||||
proc.stdout.on('data', (buf) => {
|
||||
log('%s stdout: %s', browser.name, String(buf).trim())
|
||||
|
||||
@@ -6,5 +6,9 @@
|
||||
"reporter": "../../node_modules/cypress-multi-reporters/index.js",
|
||||
"reporterOptions": {
|
||||
"configFile": "../../mocha-reporter-config.json"
|
||||
},
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"projectId": "ypt4pf",
|
||||
"baseUrl": "http://localhost:3500"
|
||||
"baseUrl": "http://localhost:3500",
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const threeTestsWithRetry = {
|
||||
},
|
||||
}
|
||||
|
||||
describe('src/cypress/runner retries mochaEvents', () => {
|
||||
describe('src/cypress/runner retries mochaEvents', { retries: 0 }, () => {
|
||||
// NOTE: for test-retries
|
||||
it('can set retry config', () => {
|
||||
runIsolatedCypress({}, { config: { retries: 1 } })
|
||||
|
||||
@@ -13,7 +13,7 @@ const threeTestsWithHooks = {
|
||||
suites: { 'suite 1': { hooks: ['before', 'beforeEach', 'afterEach', 'after'], tests: ['test 1', 'test 2', 'test 3'] } },
|
||||
}
|
||||
|
||||
describe('src/cypress/runner', () => {
|
||||
describe('src/cypress/runner', { retries: 0 }, () => {
|
||||
describe('tests finish with correct state', () => {
|
||||
describe('hook failures', () => {
|
||||
it('fail in [before]', () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const helpers = require('../support/helpers')
|
||||
|
||||
const { createCypress } = helpers
|
||||
const { runIsolatedCypress, snapshotMochaEvents } = createCypress({ config: { experimentalStudio: true, isTextTerminal: true } })
|
||||
const { runIsolatedCypress, snapshotMochaEvents } = createCypress({ config: { experimentalStudio: true, isTextTerminal: true, retries: 0 } })
|
||||
|
||||
describe('studio mocha events', () => {
|
||||
describe('studio mocha events', { retries: 0 }, () => {
|
||||
it('only runs a single test by id', () => {
|
||||
runIsolatedCypress('cypress/fixtures/studio/basic_spec.js', {
|
||||
state: {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -115,7 +115,7 @@ exports['e2e config applies defaultCommandTimeout globally 1'] = `
|
||||
|
||||
(Screenshots)
|
||||
|
||||
- /XXX/XXX/XXX/cypress/screenshots/dom_times_out_spec.js/short defaultCommandTimeo (1280x720)
|
||||
- /XXX/XXX/XXX/cypress/screenshots/dom_times_out_spec.js/short defaultCommandTimeo (1920x1080)
|
||||
ut -- times out looking for a missing element (failed).png
|
||||
|
||||
|
||||
|
||||
@@ -363,123 +363,3 @@ export const externalTest = () => {
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
exports['e2e studio / can create tests in empty spec files'] = `
|
||||
|
||||
====================================================================================================
|
||||
|
||||
(Run Starting)
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Cypress: 1.2.3 │
|
||||
│ Browser: FooBrowser 88 │
|
||||
│ Specs: 1 found (empty.spec.js) │
|
||||
│ Searched: cypress/integration/empty.spec.js │
|
||||
│ Experiments: experimentalStudio=true │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Running: empty.spec.js (1 of 1)
|
||||
|
||||
|
||||
✓ New Test
|
||||
|
||||
1 passing
|
||||
|
||||
|
||||
(Results)
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Tests: 1 │
|
||||
│ Passing: 1 │
|
||||
│ Failing: 0 │
|
||||
│ Pending: 0 │
|
||||
│ Skipped: 0 │
|
||||
│ Screenshots: 0 │
|
||||
│ Video: true │
|
||||
│ Duration: X seconds │
|
||||
│ Spec Ran: empty.spec.js │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
(Video)
|
||||
|
||||
- Started processing: Compressing to 32 CRF
|
||||
- Finished processing: /XXX/XXX/XXX/cypress/videos/empty.spec.js.mp4 (X second)
|
||||
|
||||
|
||||
====================================================================================================
|
||||
|
||||
(Run Finished)
|
||||
|
||||
|
||||
Spec Tests Passing Failing Pending Skipped
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ✔ empty.spec.js XX:XX 1 1 - - - │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
✔ All specs passed! XX:XX 1 1 - - -
|
||||
|
||||
|
||||
`
|
||||
|
||||
exports['empty.spec.js'] = `
|
||||
import { saveStudio, verifyCommandLog, verifyVisit } from '../support'
|
||||
|
||||
Cypress.config('isTextTerminal', false)
|
||||
|
||||
// this whole thing has to run outside of a test to get the proper 0 state
|
||||
// so we bind to the start event rather than using an \`it\` block
|
||||
Cypress.on('run:start', () => {
|
||||
const $document = Cypress.$(window.top.document.body)
|
||||
|
||||
// can't use Cypress commands outside of a test so we're limited to jquery
|
||||
if ($document.find('.no-tests')[0]) {
|
||||
$document.find('.open-studio')[0].click()
|
||||
} else {
|
||||
Cypress.config('isTextTerminal', true)
|
||||
Cypress.emit('run:end')
|
||||
|
||||
// use setTimeout to push to the end of the stack after render
|
||||
setTimeout(() => {
|
||||
$document.find('.runner').find('.input-active')[0].click()
|
||||
|
||||
// react is super funky when it comes to event handling
|
||||
// and this is the 'simplest' way to mock an change event
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set
|
||||
|
||||
nativeInputValueSetter.call($document.find('.runner').find('.input-active')[0], 'new.html')
|
||||
|
||||
const changeEvent = new Event('input', { bubbles: true })
|
||||
|
||||
$document.find('.runner').find('.input-active')[0].dispatchEvent(changeEvent)
|
||||
|
||||
setTimeout(() => {
|
||||
$document.find('.runner').find('.btn-submit')[0].click()
|
||||
|
||||
// we can finally use Cypress commands which makes it super easy from here on out
|
||||
cy.get('.btn', { log: false }).click({ log: false })
|
||||
|
||||
verifyVisit('new.html')
|
||||
|
||||
verifyCommandLog(2, {
|
||||
selector: '.btn',
|
||||
name: 'click',
|
||||
})
|
||||
|
||||
saveStudio('My New Test')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/* === Test Created with Cypress Studio === */
|
||||
it('My New Test', function() {
|
||||
/* ==== Generated with Cypress Studio ==== */
|
||||
cy.visit('new.html');
|
||||
cy.get('.btn').click();
|
||||
/* ==== End Cypress Studio ==== */
|
||||
});
|
||||
|
||||
`
|
||||
|
||||
@@ -34,7 +34,7 @@ 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()
|
||||
|
||||
function launchOrFork () {
|
||||
const nodeOptions = require('./lib/util/node_options')
|
||||
|
||||
@@ -261,6 +261,7 @@ module.exports = {
|
||||
'recordKey',
|
||||
'specPattern',
|
||||
'tags',
|
||||
'testingType',
|
||||
]),
|
||||
runnerCapabilities,
|
||||
}
|
||||
|
||||
@@ -428,9 +428,9 @@ export = {
|
||||
if (isHeadless) {
|
||||
args.push('--headless')
|
||||
|
||||
// set the window size for headless to a better default
|
||||
// set default headless size to 1920x1080
|
||||
// https://github.com/cypress-io/cypress/issues/6210
|
||||
args.push('--window-size=1280,720')
|
||||
args.push('--window-size=1920,1080')
|
||||
}
|
||||
|
||||
// force ipv4
|
||||
|
||||
@@ -153,6 +153,12 @@ module.exports = {
|
||||
},
|
||||
}
|
||||
|
||||
if (options.browser.isHeadless) {
|
||||
// prevents a tiny 1px padding around the window
|
||||
// causing screenshots/videos to be off by 1px
|
||||
options.resizable = false
|
||||
}
|
||||
|
||||
return _.defaultsDeep({}, options, defaults)
|
||||
},
|
||||
|
||||
@@ -161,6 +167,15 @@ module.exports = {
|
||||
_render (url, projectRoot, automation, options = {}) {
|
||||
const win = Windows.create(projectRoot, options)
|
||||
|
||||
if (options.browser.isHeadless) {
|
||||
// seemingly the only way to force headless to a certain screen size
|
||||
// electron BrowserWindow constructor is not respecting width/height options
|
||||
win.setSize(options.width, options.height)
|
||||
} else {
|
||||
// we maximize in headed mode, this is consistent with chrome+firefox headed
|
||||
win.maximize()
|
||||
}
|
||||
|
||||
automation.use(_getAutomation(win, options))
|
||||
|
||||
return this._launch(win, url, automation, options)
|
||||
@@ -235,7 +250,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 +306,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 +322,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,
|
||||
})
|
||||
|
||||
@@ -308,6 +308,37 @@ const defaultPreferences = {
|
||||
'browser.helperApps.neverAsk.saveToDisk': downloadMimeTypes,
|
||||
}
|
||||
|
||||
const FIREFOX_HEADED_USERCSS = `\
|
||||
#urlbar:not(.megabar), #urlbar.megabar > #urlbar-background, #searchbar {
|
||||
background: -moz-Field !important;
|
||||
color: -moz-FieldText !important;
|
||||
}`
|
||||
|
||||
const FIREFOX_HEADLESS_USERCSS = `\
|
||||
#urlbar {
|
||||
height: 0px !important;
|
||||
min-height: 0px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
#toolbar {
|
||||
height: 0px !important;
|
||||
min-height: 0px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
toolbar {
|
||||
height: 0px !important;
|
||||
min-height: 0px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
#titlebar {
|
||||
height: 0px !important;
|
||||
min-height: 0px !important;
|
||||
overflow: hidden !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
export function _createDetachedInstance (browserInstance: BrowserInstance): BrowserInstance {
|
||||
const detachedInstance: BrowserInstance = new EventEmitter() as BrowserInstance
|
||||
|
||||
@@ -339,6 +370,10 @@ export async function open (browser: Browser, url, options: any = {}): Promise<B
|
||||
|
||||
if (browser.isHeadless) {
|
||||
defaultLaunchOptions.args.push('-headless')
|
||||
// we don't need to specify width/height since MOZ_HEADLESS_ env vars will be set
|
||||
// and the browser will spawn maximized. The user may still supply these args to override
|
||||
// defaultLaunchOptions.args.push('--width=1920')
|
||||
// defaultLaunchOptions.args.push('--height=1081')
|
||||
}
|
||||
|
||||
debug('firefox open %o', options)
|
||||
@@ -428,18 +463,23 @@ export async function open (browser: Browser, url, options: any = {}): Promise<B
|
||||
const userCSSPath = path.join(profileDir, 'chrome')
|
||||
|
||||
if (!await fs.pathExists(path.join(userCSSPath, 'userChrome.css'))) {
|
||||
const userCss = `
|
||||
#urlbar:not(.megabar), #urlbar.megabar > #urlbar-background, #searchbar {
|
||||
background: -moz-Field !important;
|
||||
color: -moz-FieldText !important;
|
||||
}
|
||||
`
|
||||
|
||||
try {
|
||||
await fs.mkdir(userCSSPath)
|
||||
} catch {
|
||||
// probably the folder already exists, this is fine
|
||||
}
|
||||
|
||||
// if we're headed we change the yellow automation mode url bar back to a normal color
|
||||
//
|
||||
// if we're headless we use userCss to 'trick' the browser
|
||||
// into having a consistent browser window size that's near-fullscreen
|
||||
// however it unfortunately still leaves 1px of padding at the top)
|
||||
// without this trick there would be ~74px of padding at the top instead of 1px.
|
||||
//
|
||||
// TODO: allow configuring userCss through launchOptions
|
||||
|
||||
const userCss = options.browser.isHeadless ? FIREFOX_HEADLESS_USERCSS : FIREFOX_HEADED_USERCSS
|
||||
|
||||
await fs.writeFile(path.join(profileDir, 'chrome', 'userChrome.css'), userCss)
|
||||
}
|
||||
|
||||
@@ -450,7 +490,12 @@ 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 utils.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',
|
||||
MOZ_HEADLESS_HEIGHT: '1081',
|
||||
})
|
||||
|
||||
await firefoxUtil.setup({ extensions: launchOptions.extensions, url, foxdriverPort, marionettePort })
|
||||
.catch((err) => {
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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(),
|
||||
@@ -562,7 +563,7 @@ const _postInstanceTests = ({
|
||||
}
|
||||
|
||||
const createRunAndRecordSpecs = (options = {}) => {
|
||||
const { specPattern, specs, sys, browser, projectId, config, projectRoot, runAllSpecs, parallel, ciBuildId, group, project, onError } = options
|
||||
const { specPattern, specs, sys, browser, projectId, config, projectRoot, runAllSpecs, parallel, ciBuildId, group, project, onError, testingType } = options
|
||||
const recordKey = options.key
|
||||
|
||||
// we want to normalize this to an array to send to API
|
||||
@@ -585,6 +586,7 @@ const createRunAndRecordSpecs = (options = {}) => {
|
||||
return createRun({
|
||||
git,
|
||||
specs,
|
||||
testingType,
|
||||
group,
|
||||
tags,
|
||||
parallel,
|
||||
|
||||
@@ -544,8 +544,8 @@ const getChromeProps = (writeVideoFrame) => {
|
||||
const getElectronProps = (isHeaded, writeVideoFrame, onError) => {
|
||||
return _
|
||||
.chain({
|
||||
width: 1280,
|
||||
height: 720,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
show: isHeaded,
|
||||
onCrashed () {
|
||||
const err = errors.get('RENDERER_CRASHED')
|
||||
@@ -1513,7 +1513,7 @@ module.exports = {
|
||||
|
||||
const socketId = random.id()
|
||||
|
||||
const { projectRoot, record, key, ciBuildId, parallel, group, browser: browserName, tag } = options
|
||||
const { projectRoot, record, key, ciBuildId, parallel, group, browser: browserName, tag, testingType } = options
|
||||
|
||||
// this needs to be a closure over `this.exitEarly` and not a reference
|
||||
// because `this.exitEarly` gets overwritten in `this.listenForProjectEnd`
|
||||
@@ -1630,6 +1630,7 @@ module.exports = {
|
||||
browser,
|
||||
parallel,
|
||||
ciBuildId,
|
||||
testingType,
|
||||
project,
|
||||
projectId,
|
||||
projectRoot,
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
+6
-4
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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')
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -27,6 +27,7 @@ describe('e2e studio', function () {
|
||||
project,
|
||||
spec: 'extend.spec.js',
|
||||
snapshot: true,
|
||||
browser: 'electron',
|
||||
onRun (exec) {
|
||||
return exec().then(() => snapshotFile('extend.spec.js'))
|
||||
},
|
||||
@@ -37,6 +38,7 @@ describe('e2e studio', function () {
|
||||
e2e.it('creates new test', {
|
||||
project,
|
||||
spec: 'new.spec.js',
|
||||
browser: 'electron',
|
||||
snapshot: true,
|
||||
onRun (exec) {
|
||||
return exec().then(() => snapshotFile('new.spec.js'))
|
||||
@@ -47,6 +49,7 @@ describe('e2e studio', function () {
|
||||
project,
|
||||
spec: 'external.spec.js',
|
||||
snapshot: true,
|
||||
browser: 'electron',
|
||||
onRun (exec) {
|
||||
return exec()
|
||||
// we snapshot the original spec to make sure it does NOT get written there
|
||||
@@ -54,13 +57,4 @@ describe('e2e studio', function () {
|
||||
.then(() => snapshotFile('external.js', 'support'))
|
||||
},
|
||||
})
|
||||
|
||||
e2e.it('can create tests in empty spec files', {
|
||||
project,
|
||||
spec: 'empty.spec.js',
|
||||
snapshot: true,
|
||||
onRun (exec) {
|
||||
return exec().then(() => snapshotFile('empty.spec.js'))
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,10 +40,9 @@ describe('e2e headless', function () {
|
||||
})
|
||||
})
|
||||
|
||||
e2e.it('launches maximized by default in headless mode (1280x720)', {
|
||||
browser: 'chrome',
|
||||
e2e.it('launches maximized by default in headless mode (1920x1080)', {
|
||||
headed: false,
|
||||
project: Fixtures.projectPath('screen-size'),
|
||||
spec: '720p.spec.js',
|
||||
spec: 'default_size.spec.js',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -1143,9 +1143,13 @@ describe('lib/cypress', () => {
|
||||
setProxy: sinon.stub().resolves(),
|
||||
setUserAgent: sinon.stub(),
|
||||
on: sinon.stub(),
|
||||
removeListener: sinon.stub(),
|
||||
},
|
||||
}
|
||||
|
||||
ee.maximize = sinon.stub
|
||||
ee.setSize = sinon.stub
|
||||
|
||||
sinon.stub(browserUtils, 'launch').resolves(ee)
|
||||
sinon.stub(Windows, 'create').returns(ee)
|
||||
})
|
||||
|
||||
@@ -474,12 +474,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
|
||||
|
||||
@@ -6,7 +6,7 @@ const http = require('http')
|
||||
const Jimp = require('jimp')
|
||||
const path = require('path')
|
||||
const Promise = require('bluebird')
|
||||
const { useFixedFirefoxResolution } = require('../../../utils')
|
||||
const { useFixedBrowserLaunchSize } = require('../../../utils')
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
@@ -53,7 +53,7 @@ module.exports = (on, config) => {
|
||||
})
|
||||
|
||||
on('before:browser:launch', (browser, options) => {
|
||||
useFixedFirefoxResolution(browser, options, config)
|
||||
useFixedBrowserLaunchSize(browser, options, config)
|
||||
|
||||
if (browser.family === 'firefox' && process.env.FIREFOX_FORCE_STRICT_SAMESITE) {
|
||||
// @see https://www.jardinesoftware.net/2019/10/28/samesite-by-default-in-2020/
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -1,6 +1,6 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
const { useFixedFirefoxResolution } = require('../../../utils')
|
||||
const { useFixedBrowserLaunchSize } = require('../../../utils')
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
@@ -11,7 +11,7 @@ module.exports = (on, config) => {
|
||||
}
|
||||
|
||||
on('before:browser:launch', (browser, options) => {
|
||||
useFixedFirefoxResolution(browser, options, config)
|
||||
useFixedBrowserLaunchSize(browser, options, config)
|
||||
|
||||
return options
|
||||
})
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
describe('windowSize', () => {
|
||||
it('spawns 720p', () => {
|
||||
// assert the browser was spawned at 1280x720 and is full size
|
||||
// normally e2e tests spawn at fixed size, but this spec should be spawned without passing any width/height arguments in plugins file.
|
||||
expect({
|
||||
innerWidth: top.window.innerWidth,
|
||||
innerHeight: top.window.innerHeight,
|
||||
availWidth: top.screen.availWidth,
|
||||
availHeight: top.screen.availHeight,
|
||||
screenWidth: top.screen.width,
|
||||
screenHeight: top.screen.height,
|
||||
}).deep.eq({
|
||||
innerWidth: 1280,
|
||||
innerHeight: 720,
|
||||
availWidth: 1280,
|
||||
availHeight: 720,
|
||||
screenWidth: 1280,
|
||||
screenHeight: 720,
|
||||
})
|
||||
})
|
||||
})
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
describe('windowSize', () => {
|
||||
it('spawns with correct default size', () => {
|
||||
// assert the browser was spawned at 1920x1080 and is full size
|
||||
// normally e2e tests spawn at fixed size, but this spec should be spawned without passing any width/height arguments in plugins file.
|
||||
// TODO: look into fixing screen/available height and width
|
||||
expect({
|
||||
innerWidth: top.window.innerWidth,
|
||||
innerHeight: top.window.innerHeight,
|
||||
// screenWidth: top.screen.width,
|
||||
// screenHeight: top.screen.height,
|
||||
// availWidth: top.screen.availWidth,
|
||||
// availHeight: top.screen.availHeight,
|
||||
}).deep.eq({
|
||||
innerWidth: 1920,
|
||||
innerHeight: 1080,
|
||||
// screenWidth: 1920,
|
||||
// screenHeight: 1080,
|
||||
// availWidth: 1920,
|
||||
// availHeight: 1080,
|
||||
})
|
||||
})
|
||||
})
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
import { saveStudio, verifyCommandLog, verifyVisit } from '../support'
|
||||
|
||||
Cypress.config('isTextTerminal', false)
|
||||
|
||||
// this whole thing has to run outside of a test to get the proper 0 state
|
||||
// so we bind to the start event rather than using an `it` block
|
||||
Cypress.on('run:start', () => {
|
||||
const $document = Cypress.$(window.top.document.body)
|
||||
|
||||
// can't use Cypress commands outside of a test so we're limited to jquery
|
||||
if ($document.find('.no-tests')[0]) {
|
||||
$document.find('.open-studio')[0].click()
|
||||
} else {
|
||||
Cypress.config('isTextTerminal', true)
|
||||
Cypress.emit('run:end')
|
||||
|
||||
// use setTimeout to push to the end of the stack after render
|
||||
setTimeout(() => {
|
||||
$document.find('.runner').find('.input-active')[0].click()
|
||||
|
||||
// react is super funky when it comes to event handling
|
||||
// and this is the 'simplest' way to mock an change event
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set
|
||||
|
||||
nativeInputValueSetter.call($document.find('.runner').find('.input-active')[0], 'new.html')
|
||||
|
||||
const changeEvent = new Event('input', { bubbles: true })
|
||||
|
||||
$document.find('.runner').find('.input-active')[0].dispatchEvent(changeEvent)
|
||||
|
||||
setTimeout(() => {
|
||||
$document.find('.runner').find('.btn-submit')[0].click()
|
||||
|
||||
// we can finally use Cypress commands which makes it super easy from here on out
|
||||
cy.get('.btn', { log: false }).click({ log: false })
|
||||
|
||||
verifyVisit('new.html')
|
||||
|
||||
verifyCommandLog(2, {
|
||||
selector: '.btn',
|
||||
name: 'click',
|
||||
})
|
||||
|
||||
saveStudio('My New Test')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,11 +1,19 @@
|
||||
module.exports = {
|
||||
useFixedFirefoxResolution (browser, options, config) {
|
||||
if (browser.family === 'firefox' && !config.env['NO_RESIZE']) {
|
||||
useFixedBrowserLaunchSize (browser, options, config) {
|
||||
if (config.env['NO_RESIZE']) return
|
||||
|
||||
if (browser.family === 'firefox') {
|
||||
// this is needed to ensure correct error screenshot / video recording
|
||||
// resolution of exactly 1280x720 (height must account for firefox url bar)
|
||||
options.args = options.args.concat(
|
||||
['-width', '1280', '-height', '794'],
|
||||
// resolution of exactly 1280x720
|
||||
// (height must account for firefox url bar, which we can only shrink to 1px)
|
||||
options.args.push(
|
||||
'-width', '1280', '-height', '721',
|
||||
)
|
||||
} else if (browser.name === 'electron') {
|
||||
options.preferences.width = 1280
|
||||
options.preferences.height = 720
|
||||
} else if (browser.family === 'chromium') {
|
||||
options.args.push('--window-size=1280,720')
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -416,7 +416,6 @@ const e2e = {
|
||||
|
||||
if (process.env.NO_EXIT) {
|
||||
Fixtures.scaffoldWatch()
|
||||
process.env.CYPRESS_INTERNAL_E2E_TESTS
|
||||
}
|
||||
|
||||
sinon.stub(process, 'exit')
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -106,7 +106,7 @@ describe('lib/browsers/chrome', () => {
|
||||
|
||||
expect(args).to.include.members([
|
||||
'--headless',
|
||||
'--window-size=1280,720',
|
||||
'--window-size=1920,1080',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,6 +22,9 @@ describe('lib/browsers/electron', () => {
|
||||
some: 'var',
|
||||
projectRoot: '/foo/',
|
||||
onWarning: sinon.stub().returns(),
|
||||
browser: {
|
||||
isHeadless: false,
|
||||
},
|
||||
}
|
||||
|
||||
this.automation = new Automation('foo', 'bar', 'baz')
|
||||
@@ -278,14 +281,16 @@ describe('lib/browsers/electron', () => {
|
||||
|
||||
context('._render', () => {
|
||||
beforeEach(function () {
|
||||
this.newWin = {}
|
||||
this.newWin = {
|
||||
maximize: sinon.stub(),
|
||||
setSize: sinon.stub(),
|
||||
}
|
||||
|
||||
sinon.stub(menu, 'set')
|
||||
sinon.stub(electron, '_setProxy').resolves()
|
||||
sinon.stub(electron, '_launch').resolves()
|
||||
|
||||
return sinon.stub(Windows, 'create')
|
||||
.withArgs(this.options.projectRoot, this.options)
|
||||
.returns(this.newWin)
|
||||
})
|
||||
|
||||
@@ -293,18 +298,29 @@ describe('lib/browsers/electron', () => {
|
||||
return electron._render(this.url, this.options.projectRoot, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(Windows.create).to.be.calledWith(this.options.projectRoot, this.options)
|
||||
|
||||
expect(this.newWin.maximize).called
|
||||
expect(this.newWin.setSize).not.called
|
||||
expect(electron._launch).to.be.calledWith(this.newWin, this.url, this.automation, this.options)
|
||||
})
|
||||
})
|
||||
|
||||
it('calls setSize on electron window if headless', function () {
|
||||
const options = { ...this.options, browser: { isHeadless: true }, width: 100, height: 200 }
|
||||
|
||||
return electron._render(this.url, this.options.projectRoot, this.automation, options)
|
||||
.then(() => {
|
||||
expect(this.newWin.maximize).not.called
|
||||
expect(this.newWin.setSize).calledWith(100, 200)
|
||||
})
|
||||
})
|
||||
|
||||
it('registers onRequest automation middleware', function () {
|
||||
sinon.spy(this.automation, 'use')
|
||||
|
||||
return electron._render(this.url, this.options.projectRoot, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(Windows.create).to.be.calledWith(this.options.projectRoot, this.options)
|
||||
expect(this.automation.use).to.be.called
|
||||
|
||||
expect(this.automation.use.lastCall.args[0].onRequest).to.be.a('function')
|
||||
})
|
||||
})
|
||||
@@ -366,14 +382,14 @@ describe('lib/browsers/electron', () => {
|
||||
})
|
||||
|
||||
it('.onFocus', function () {
|
||||
let opts = electron._defaultOptions('/foo', this.state, { show: true })
|
||||
let opts = electron._defaultOptions('/foo', this.state, { show: true, browser: {} })
|
||||
|
||||
opts.onFocus()
|
||||
expect(menu.set).to.be.calledWith({ withDevTools: true })
|
||||
|
||||
menu.set.reset()
|
||||
|
||||
opts = electron._defaultOptions('/foo', this.state, { show: false })
|
||||
opts = electron._defaultOptions('/foo', this.state, { show: false, browser: {} })
|
||||
opts.onFocus()
|
||||
|
||||
expect(menu.set).not.to.be.called
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -101,9 +101,9 @@ describe('lib/modes/run', () => {
|
||||
it('sets width and height', () => {
|
||||
const props = runMode.getElectronProps()
|
||||
|
||||
expect(props.width).to.eq(1280)
|
||||
expect(props.width).to.eq(1920)
|
||||
|
||||
expect(props.height).to.eq(720)
|
||||
expect(props.height).to.eq(1080)
|
||||
})
|
||||
|
||||
it('sets show to boolean', () => {
|
||||
|
||||
+3
-3
@@ -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()
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,5 +4,9 @@
|
||||
"reporter": "../../node_modules/cypress-multi-reporters/index.js",
|
||||
"reporterOptions": {
|
||||
"configFile": "../../mocha-reporter-config.json"
|
||||
},
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 0
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user