mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-21 14:41:00 -06:00
feat: component testing (#14479)
Co-authored-by: Jessica Sachs <jess@jessicasachs.io> Co-authored-by: Barthélémy Ledoux <bart@cypress.io> Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com> Co-authored-by: Zach Bloomquist <github@chary.us> Co-authored-by: Dmitriy Kovalenko <dmtr.kovalenko@outlook.com> Co-authored-by: ElevateBart <ledouxb@gmail.com> Co-authored-by: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
This commit is contained in:
@@ -7,8 +7,11 @@
|
||||
"plugin:@cypress/dev/tests"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-useless-constructor": "off",
|
||||
"prefer-spread": "off",
|
||||
"prefer-rest-params": "off"
|
||||
"prefer-rest-params": "off",
|
||||
"no-useless-constructor": "off"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -1,6 +1,12 @@
|
||||
{
|
||||
"eslint.alwaysShowStatus": true,
|
||||
"eslint.validate": ["json"],
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"json"
|
||||
],
|
||||
"eslint.enable": true,
|
||||
// this project does not use Prettier
|
||||
// thus set all settings to disable accidentally running Prettier
|
||||
|
||||
39
.vscode/tasks.json
vendored
Normal file
39
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "rename yarn workspace packages",
|
||||
"type": "shell",
|
||||
"command": "node ./scripts/rename-workspace-packages --file ${file}",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "silent",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "decaffeinate-bulk file",
|
||||
"type": "shell",
|
||||
"command": "yarn decaffeinate-bulk convert --file ${file}",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "decaffeinate-bulk multiple files",
|
||||
"type": "shell",
|
||||
"command": "yarn decaffeinate-bulk convert --file ${file} ${file}",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "decaffeinate-bulk dir",
|
||||
"type": "shell",
|
||||
"command": "yarn decaffeinate-bulk --dir ${fileDirname} convert",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
35
.vscode/terminals.json
vendored
35
.vscode/terminals.json
vendored
@@ -2,19 +2,43 @@
|
||||
"autorun": false,
|
||||
"terminals": [
|
||||
{
|
||||
"name": "cypress open",
|
||||
"name": "yarn",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"execute": true,
|
||||
"cwd": "[cwd]",
|
||||
"command": "yarn"
|
||||
},
|
||||
{
|
||||
"name": "cypress open (E2E)",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"execute": true,
|
||||
"command": "yarn cypress:open"
|
||||
},
|
||||
{
|
||||
"name": "cypress run",
|
||||
"name": "cypress run (E2E)",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"execute": false,
|
||||
"command": "yarn cypress:run --project ../project"
|
||||
},
|
||||
{
|
||||
"name": "cypress open (CT)",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"execute": true,
|
||||
"cwd": "[cwd]/packages/server-ct",
|
||||
"command": "yarn cypress:open"
|
||||
},
|
||||
{
|
||||
"name": "packages/server test-e2e",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"execute": false,
|
||||
"cwd": "[cwd]/packages/server",
|
||||
"command": "yarn test-e2e [fileBasename]"
|
||||
},
|
||||
{
|
||||
"name": "packages/server test-watch",
|
||||
"focus": true,
|
||||
@@ -38,6 +62,13 @@
|
||||
"cwd": "[cwd]/packages/runner",
|
||||
"command": "yarn watch"
|
||||
},
|
||||
{
|
||||
"name": "packages/runner-ct watch",
|
||||
"focus": true,
|
||||
"onlySingle": true,
|
||||
"cwd": "[cwd]/packages/runner-ct",
|
||||
"command": "yarn watch"
|
||||
},
|
||||
{
|
||||
"name": "packages/driver cypress open",
|
||||
"focus": true,
|
||||
|
||||
59
circle.yml
59
circle.yml
@@ -8,6 +8,9 @@ macBuildFilters: &macBuildFilters
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- include-electron-node-version
|
||||
- sem-next-ver
|
||||
|
||||
defaults: &defaults
|
||||
@@ -35,6 +38,8 @@ testBinaryFirefox: &testBinaryFirefox
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build-npm-package
|
||||
@@ -642,6 +647,20 @@ jobs:
|
||||
path: /tmp/cypress
|
||||
- store-npm-logs
|
||||
|
||||
server-ct-unit-tests:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run: yarn test-unit --scope @packages/server-ct
|
||||
- verify-mocha-results:
|
||||
expectedResultCount: 1
|
||||
- store_test_results:
|
||||
path: /tmp/cypress
|
||||
- store-npm-logs
|
||||
|
||||
server-integration-tests:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
@@ -893,6 +912,16 @@ jobs:
|
||||
working_directory: npm/webpack-preprocessor/examples/react-app
|
||||
- store-npm-logs
|
||||
|
||||
npm-webpack-dev-server:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- check-conditional-ci
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/webpack-dev-server test
|
||||
|
||||
npm-webpack-batteries-included-preprocessor:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@@ -914,7 +943,8 @@ jobs:
|
||||
command: yarn workspace @cypress/vue build
|
||||
- run:
|
||||
name: Run tests
|
||||
command: yarn workspace @cypress/vue test
|
||||
command: yarn test
|
||||
working_directory: npm/vue
|
||||
- store-npm-logs
|
||||
|
||||
npm-react:
|
||||
@@ -1711,6 +1741,9 @@ linux-workflow: &linux-workflow
|
||||
requires:
|
||||
- build
|
||||
|
||||
- npm-webpack-dev-server:
|
||||
requires:
|
||||
- build
|
||||
- npm-webpack-preprocessor:
|
||||
requires:
|
||||
- build
|
||||
@@ -1808,6 +1841,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build
|
||||
@@ -1826,6 +1861,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build
|
||||
@@ -1838,6 +1875,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build-npm-package
|
||||
@@ -1850,6 +1889,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build-binary
|
||||
@@ -1907,6 +1948,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- upload-npm-package
|
||||
@@ -1917,6 +1960,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- upload-npm-package
|
||||
@@ -1926,6 +1971,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build-npm-package
|
||||
@@ -1936,6 +1983,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build-npm-package
|
||||
@@ -1962,6 +2011,8 @@ linux-workflow: &linux-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- build-npm-package
|
||||
@@ -2056,6 +2107,8 @@ mac-workflow: &mac-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- Mac NPM package
|
||||
@@ -2069,6 +2122,8 @@ mac-workflow: &mac-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- Mac NPM package upload
|
||||
@@ -2082,6 +2137,8 @@ mac-workflow: &mac-workflow
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- v6.0-release
|
||||
- feat/component-testing
|
||||
- sem-next-ver
|
||||
requires:
|
||||
- Mac NPM package upload
|
||||
|
||||
@@ -35,6 +35,7 @@ exports['shows help for open --foo 1'] = `
|
||||
-P, --project <project-path> path to the project
|
||||
--dev runs cypress in development and bypasses
|
||||
binary check
|
||||
-ct, --component-testing Cypress Component Testing mode (alpha)
|
||||
-h, --help display help for command
|
||||
-------
|
||||
stderr:
|
||||
|
||||
@@ -401,6 +401,7 @@ module.exports = {
|
||||
.option('--global', text('global'))
|
||||
.option('-p, --port <port>', text('port'))
|
||||
.option('-P, --project <project-path>', text('project'))
|
||||
.option('--run-project <project-path>', 'like `project`, but forces Cypress into runmode')
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.action((opts) => {
|
||||
debug('opening Cypress')
|
||||
@@ -422,6 +423,7 @@ module.exports = {
|
||||
.option('-p, --port <port>', text('port'))
|
||||
.option('-P, --project <project-path>', text('project'))
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.option('-ct, --component-testing', 'Cypress Component Testing mode (alpha)')
|
||||
.action((opts) => {
|
||||
debug('opening Cypress')
|
||||
require('./exec/open')
|
||||
|
||||
@@ -37,6 +37,10 @@ module.exports = {
|
||||
|
||||
if (isComponentTesting) {
|
||||
args.push('--componentTesting')
|
||||
|
||||
if (options.runProject) {
|
||||
args.push('--run-project', options.runProject)
|
||||
}
|
||||
}
|
||||
|
||||
debug('opening from options %j', options)
|
||||
|
||||
@@ -2,11 +2,15 @@ const os = require('os')
|
||||
const Promise = require('bluebird')
|
||||
const Xvfb = require('@cypress/xvfb')
|
||||
const { stripIndent } = require('common-tags')
|
||||
const debug = require('debug')('cypress:cli')
|
||||
const debugXvfb = require('debug')('cypress:xvfb')
|
||||
const Debug = require('debug')
|
||||
const { throwFormErrorText, errors } = require('../errors')
|
||||
const util = require('../util')
|
||||
|
||||
const debug = Debug('cypress:cli')
|
||||
const debugXvfb = Debug('cypress:xvfb')
|
||||
|
||||
debug.Debug = debugXvfb.Debug = Debug
|
||||
|
||||
const xvfbOptions = {
|
||||
timeout: 30000, // milliseconds
|
||||
// need to explicitly define screen otherwise electron will crash
|
||||
|
||||
@@ -220,6 +220,7 @@ const parseOpts = (opts) => {
|
||||
'reporter',
|
||||
'reporterOptions',
|
||||
'record',
|
||||
'runProject',
|
||||
'spec',
|
||||
'tag')
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@cypress/listr-verbose-renderer": "^0.4.1",
|
||||
"@cypress/request": "^2.88.5",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/node": "12.12.50",
|
||||
"@types/sinonjs__fake-timers": "^6.0.1",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
"arch": "^2.1.2",
|
||||
@@ -35,7 +36,7 @@
|
||||
"commander": "^5.1.0",
|
||||
"common-tags": "^1.8.0",
|
||||
"dayjs": "^1.9.3",
|
||||
"debug": "^4.1.1",
|
||||
"debug": "4.3.2",
|
||||
"eventemitter2": "^6.4.2",
|
||||
"execa": "^4.0.2",
|
||||
"executable": "^4.1.1",
|
||||
|
||||
@@ -9,9 +9,20 @@ describe('lib/exec/xvfb', function () {
|
||||
})
|
||||
|
||||
context('debugXvfb', function () {
|
||||
let { Debug } = xvfb._debugXvfb
|
||||
let { namespaces } = Debug
|
||||
|
||||
beforeEach(() => {
|
||||
Debug.enable(namespaces)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Debug.enable(namespaces)
|
||||
})
|
||||
|
||||
it('outputs when enabled', function () {
|
||||
sinon.stub(process.stderr, 'write').returns(undefined)
|
||||
sinon.stub(xvfb._debugXvfb, 'enabled').value(true)
|
||||
Debug.enable(xvfb._debugXvfb.namespace)
|
||||
|
||||
xvfb._xvfb._onStderrData('asdf')
|
||||
|
||||
@@ -21,7 +32,7 @@ describe('lib/exec/xvfb', function () {
|
||||
|
||||
it('does not output when disabled', function () {
|
||||
sinon.stub(process.stderr, 'write')
|
||||
sinon.stub(xvfb._debugXvfb, 'enabled').value(false)
|
||||
Debug.disable()
|
||||
|
||||
xvfb._xvfb._onStderrData('asdf')
|
||||
|
||||
|
||||
13
cli/types/cy-http.d.ts
vendored
Normal file
13
cli/types/cy-http.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* This file should be deleted as soon as the serever
|
||||
* TODO: delete this file when ResolvedDevServerConfig.server is converted to closeServer
|
||||
*/
|
||||
|
||||
/// <reference types="node" />
|
||||
import * as cyUtilsHttp from 'http'
|
||||
export = cyUtilsHttp
|
||||
/**
|
||||
* namespace created to bridge nodeJs.http typings so that
|
||||
* we can type http Server in CT
|
||||
*/
|
||||
export as namespace cyUtilsHttp
|
||||
19
cli/types/cypress.d.ts
vendored
19
cli/types/cypress.d.ts
vendored
@@ -1,3 +1,4 @@
|
||||
/// <reference path="./cy-http.d.ts" />
|
||||
/// <reference path="./cypress-npm-api.d.ts" />
|
||||
|
||||
declare namespace Cypress {
|
||||
@@ -5131,6 +5132,23 @@ declare namespace Cypress {
|
||||
tag?: string
|
||||
}
|
||||
|
||||
interface DevServerOptions {
|
||||
specs: Spec[]
|
||||
config: {
|
||||
supportFile?: string
|
||||
projectRoot: string
|
||||
webpackDevServerPublicPathRoute: string
|
||||
},
|
||||
devServerEvents: NodeJS.EventEmitter,
|
||||
}
|
||||
|
||||
interface ResolvedDevServerConfig {
|
||||
port: number
|
||||
// TODO: when removing server and replacing it by close Function,
|
||||
// delete the cy-http.d.ts file. It's a hack.
|
||||
server: cyUtilsHttp.Server
|
||||
}
|
||||
|
||||
interface PluginEvents {
|
||||
(action: 'after:run', fn: (results: CypressCommandLine.CypressRunResult | CypressCommandLine.CypressFailedRunResult) => void | Promise<void>): void
|
||||
(action: 'after:screenshot', fn: (details: ScreenshotDetails) => void | AfterScreenshotReturnObject | Promise<AfterScreenshotReturnObject>): void
|
||||
@@ -5139,6 +5157,7 @@ declare namespace Cypress {
|
||||
(action: 'before:spec', fn: (spec: Spec) => void | Promise<void>): void
|
||||
(action: 'before:browser:launch', fn: (browser: Browser, browserLaunchOptions: BrowserLaunchOptions) => void | BrowserLaunchOptions | Promise<BrowserLaunchOptions>): void
|
||||
(action: 'file:preprocessor', fn: (file: FileObject) => string | Promise<string>): void
|
||||
(action: 'dev-server:start', fn: (file: DevServerOptions) => Promise<ResolvedDevServerConfig>): void
|
||||
(action: 'task', tasks: Tasks): void
|
||||
}
|
||||
|
||||
|
||||
@@ -299,6 +299,42 @@ module.exports = {
|
||||
'mocha/no-global-tests': 'error',
|
||||
'@cypress/dev/skip-comment': 'error',
|
||||
},
|
||||
overrides: [{
|
||||
files: '*.tsx',
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
ecmaFeatures: {
|
||||
jsx: true, // Allows for the parsing of JSX
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'react',
|
||||
],
|
||||
rules: {
|
||||
'no-unused-vars': 'off', // avoid interface imports to be warned against
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'react/jsx-curly-spacing': 'error',
|
||||
'react/jsx-equals-spacing': 'error',
|
||||
'react/jsx-no-duplicate-props': 'error',
|
||||
'react/jsx-no-undef': 'error',
|
||||
'react/jsx-pascal-case': 'error',
|
||||
'react/jsx-uses-react': 'error',
|
||||
'react/jsx-uses-vars': 'error',
|
||||
'react/jsx-wrap-multilines': 'error',
|
||||
'react/no-unknown-property': 'error',
|
||||
'react/prefer-es6-class': 'error',
|
||||
'react/react-in-jsx-scope': 'error',
|
||||
'react/require-render-return': 'error',
|
||||
},
|
||||
}],
|
||||
},
|
||||
|
||||
react: {
|
||||
@@ -318,7 +354,6 @@ module.exports = {
|
||||
rules: {
|
||||
'react/jsx-curly-spacing': 'error',
|
||||
'react/jsx-equals-spacing': 'error',
|
||||
'react/jsx-filename-extension': 'error',
|
||||
'react/jsx-no-duplicate-props': 'error',
|
||||
'react/jsx-no-undef': 'error',
|
||||
'react/jsx-pascal-case': 'error',
|
||||
@@ -329,6 +364,7 @@ module.exports = {
|
||||
'react/prefer-es6-class': 'error',
|
||||
'react/react-in-jsx-scope': 'error',
|
||||
'react/require-render-return': 'error',
|
||||
'react/jsx-filename-extension': 'error',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"projectId": "z9dxah",
|
||||
"testFiles": "**/*spec.{js,jsx,ts,tsx}",
|
||||
"env": {
|
||||
"reactDevtools": true,
|
||||
"cypress-react-selector": {
|
||||
"root": "#cypress-root"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mount } from '@cypress/react'
|
||||
import { Timer } from './Timer'
|
||||
import { TimerView } from './timer-view'
|
||||
|
||||
describe('MobX v6', { viewportWidth: 200, viewportHeight: 100 }, () => {
|
||||
describe('MobX v6', () => {
|
||||
context('TimerView', () => {
|
||||
it('increments every second', () => {
|
||||
const myTimer = new Timer()
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
button {
|
||||
height: 50px;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
import React from 'react'
|
||||
import { createMount, mount } from '@cypress/react'
|
||||
|
||||
describe('cssFile', () => {
|
||||
it('is loaded', () => {
|
||||
const Component = () => <button className="green">Green button</button>
|
||||
|
||||
mount(<Component />, {
|
||||
cssFiles: 'cypress/component/basic/styles/css-file/index.css',
|
||||
})
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
|
||||
it('cssFile is for loading a single file', () => {
|
||||
const Component = () => <button className="green">Green button</button>
|
||||
|
||||
mount(<Component />, {
|
||||
cssFile: 'cypress/component/basic/styles/css-file/index.css',
|
||||
})
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
|
||||
it('allows loading several CSS files', () => {
|
||||
const Component = () => {
|
||||
return (
|
||||
<button className="green">Large green button</button>
|
||||
)
|
||||
}
|
||||
|
||||
mount(<Component />, {
|
||||
cssFiles: [
|
||||
'cypress/component/basic/styles/css-file/base.css',
|
||||
'cypress/component/basic/styles/css-file/index.css',
|
||||
],
|
||||
log: false,
|
||||
})
|
||||
|
||||
// check the style from the first css file
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.invoke('css', 'height')
|
||||
.should((value) => {
|
||||
// round the height, since in real browser it is never exactly 50
|
||||
expect(parseFloat(value), 'height is 50px').to.be.closeTo(50, 1)
|
||||
})
|
||||
|
||||
// and should have style from the second css file
|
||||
cy.get('button').and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
|
||||
it('resets the style', () => {
|
||||
const Component = () => {
|
||||
return (
|
||||
<button className="green">Large green button</button>
|
||||
)
|
||||
}
|
||||
|
||||
mount(<Component />)
|
||||
// the component should NOT have CSS styles
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.invoke('css', 'height')
|
||||
.should((value) => {
|
||||
expect(parseFloat(value), 'height is < 30px').to.be.lessThan(30)
|
||||
})
|
||||
})
|
||||
|
||||
context('Using createMount to simplify global css experience', () => {
|
||||
const mount = createMount({
|
||||
cssFiles: 'cypress/component/basic/styles/css-file/index.css',
|
||||
})
|
||||
|
||||
it('createMount green button', () => {
|
||||
const Component = () => <button className="green">Green button</button>
|
||||
|
||||
mount(<Component />)
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
|
||||
it('createMount blue button', () => {
|
||||
const Component = () => <button className="blue">blue button</button>
|
||||
|
||||
mount(<Component />)
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'blue')
|
||||
.and('have.css', 'background-color', 'rgb(0, 0, 255)')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,7 +0,0 @@
|
||||
button.green {
|
||||
background-color: #00ff00;
|
||||
}
|
||||
|
||||
button.blue {
|
||||
background-color: #0000ff;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
import React from 'react'
|
||||
import { mount } from '@cypress/react'
|
||||
|
||||
describe('stylesheets', () => {
|
||||
const baseUrl = '/__root/cypress/component/basic/styles/css-file/base.css'
|
||||
const indexUrl = '/__root/cypress/component/basic/styles/css-file/index.css'
|
||||
|
||||
context('options.stylesheet', () => {
|
||||
it('options.stylesheet string', () => {
|
||||
const Component = () => <button className="green">Green button</button>
|
||||
|
||||
mount(<Component />, {
|
||||
stylesheet: indexUrl,
|
||||
})
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
|
||||
it('options.stylesheet []', () => {
|
||||
const Component = () => <button className="green">Green button</button>
|
||||
|
||||
mount(<Component />, {
|
||||
stylesheet: [indexUrl],
|
||||
})
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
})
|
||||
|
||||
context('options.stylesheets', () => {
|
||||
it('allows loading several CSS files', () => {
|
||||
const Component = () => {
|
||||
return (
|
||||
<button className="green">Large green button</button>
|
||||
)
|
||||
}
|
||||
|
||||
mount(<Component />, {
|
||||
stylesheets: [baseUrl, indexUrl],
|
||||
})
|
||||
|
||||
// check the style from the first css file
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.invoke('css', 'height')
|
||||
.should((value) => {
|
||||
// round the height, since in real browser it is never exactly 50
|
||||
expect(parseFloat(value), 'height is 50px').to.be.closeTo(50, 1)
|
||||
})
|
||||
|
||||
// and should have style from the second css file
|
||||
cy.get('button').and('have.css', 'background-color', 'rgb(0, 255, 0)')
|
||||
})
|
||||
|
||||
it('resets the style', () => {
|
||||
const Component = () => {
|
||||
return (
|
||||
<button className="green">Large green button</button>
|
||||
)
|
||||
}
|
||||
|
||||
mount(<Component />)
|
||||
// the component should NOT have CSS styles
|
||||
|
||||
cy.get('button')
|
||||
.should('have.class', 'green')
|
||||
.invoke('css', 'height')
|
||||
.should((value) => {
|
||||
// meaning: the style has been reset
|
||||
expect(parseFloat(value), 'height is < 30px').to.be.lessThan(30)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
5
npm/react/cypress/component/smoke-spec.js
Normal file
5
npm/react/cypress/component/smoke-spec.js
Normal file
@@ -0,0 +1,5 @@
|
||||
describe('Smoke Test', () => {
|
||||
it('does not use the mount command', () => {
|
||||
expect(true).to.eq(true)
|
||||
})
|
||||
})
|
||||
23
npm/react/cypress/component/viewport-spec.js
Normal file
23
npm/react/cypress/component/viewport-spec.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const viewportWidth = 200
|
||||
const viewportHeight = 100
|
||||
|
||||
describe('cypress.json viewport',
|
||||
{ viewportWidth, viewportHeight },
|
||||
() => {
|
||||
it('should have the correct dimensions', () => {
|
||||
// cy.should cannot be the first cy command we run
|
||||
cy.window().should((w) => {
|
||||
expect(w.innerWidth).to.eq(viewportWidth)
|
||||
expect(w.innerHeight).to.eq(viewportHeight)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('cy.viewport', () => {
|
||||
it('should resize the viewport', () => {
|
||||
cy.viewport(viewportWidth, viewportHeight).should(() => {
|
||||
expect(window.innerWidth).to.eq(viewportWidth)
|
||||
expect(window.innerHeight).to.eq(viewportHeight)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2,4 +2,4 @@
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
const babelConfig = require('../../babel.config.js')
|
||||
|
||||
/** @type import("webpack").Configuration */
|
||||
const webpackOptions = {
|
||||
const webpackConfig = {
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.jsx', '.tsx'],
|
||||
},
|
||||
@@ -55,15 +55,11 @@ const webpackOptions = {
|
||||
},
|
||||
}
|
||||
|
||||
const options = {
|
||||
// send in the options from your webpack.config.js, so it works the same
|
||||
// as your app's code
|
||||
webpackOptions,
|
||||
watchOptions: {},
|
||||
}
|
||||
|
||||
/**
|
||||
* @type Cypress.PluginConfig
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
on('file:preprocessor', webpackPreprocessor(options))
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -15,5 +15,3 @@
|
||||
|
||||
// custom commands provided by this package, built from TypeScript code in "lib"
|
||||
// using "npm run transpile"
|
||||
import '@cypress/react/hooks'
|
||||
import 'cypress-react-selector'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,18 +6,22 @@
|
||||
"scripts": {
|
||||
"build": "rimraf dist && yarn transpile",
|
||||
"build-prod": "yarn build",
|
||||
"cy:open": "node ../../scripts/cypress open",
|
||||
"cy:open": "node ../../scripts/start.js --component-testing --project ${PWD}",
|
||||
"cy:open:debug": "NODE_OPTIONS=--max-http-header-size=1048576 node --inspect-brk ../../scripts/start.js --component-testing --project ${PWD}",
|
||||
"cy:run": "node ../../scripts/cypress.js open-ct --run-project ${PWD}",
|
||||
"cy:run:debug": "NODE_OPTIONS=--max-http-header-size=1048576 node --inspect-brk ../../scripts/start.js --component-testing --run-project ${PWD}",
|
||||
"pretest": "yarn transpile",
|
||||
"test": "node ../../scripts/cypress run",
|
||||
"test": "yarn cy:run",
|
||||
"transpile": "tsc",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-modules-commonjs": "7.12.1",
|
||||
"@cypress/code-coverage": "3.8.6",
|
||||
"@cypress/webpack-dev-server": "0.0.0-development",
|
||||
"@cypress/webpack-preprocessor": "0.0.0-development",
|
||||
"babel-plugin-istanbul": "6.0.0",
|
||||
"debug": "4.3.1",
|
||||
"debug": "4.3.2",
|
||||
"find-up": "5.0.0",
|
||||
"find-webpack": "2.2.1",
|
||||
"mime-types": "2.1.26",
|
||||
@@ -45,7 +49,7 @@
|
||||
"@types/chalk": "2.2.0",
|
||||
"@types/inquirer": "7.3.1",
|
||||
"@types/mock-fs": "4.10.0",
|
||||
"@types/node": "9.6.49",
|
||||
"@types/node": "12.12.50",
|
||||
"@types/semver": "7.3.4",
|
||||
"arg": "4.1.3",
|
||||
"autoprefixer": "9.7.6",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-ignore
|
||||
const unfetch = require('unfetch/dist/unfetch.js')
|
||||
// @ts-ignore
|
||||
const isComponentSpec = () => Cypress.spec.specType === 'component'
|
||||
const isComponentSpec = () => true
|
||||
|
||||
// When running component specs, we cannot allow "cy.visit"
|
||||
// because it will wipe out our preparation work, and does not make much sense
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
export * from './mount'
|
||||
|
||||
export * from './mountHook'
|
||||
|
||||
/** @deprecated */
|
||||
export { default } from './mount'
|
||||
|
||||
@@ -2,10 +2,12 @@ import * as React from 'react'
|
||||
import ReactDOM, { unmountComponentAtNode } from 'react-dom'
|
||||
import getDisplayName from './getDisplayName'
|
||||
import { injectStylesBeforeElement } from './utils'
|
||||
import './hooks'
|
||||
import 'cypress-react-selector'
|
||||
|
||||
const rootId = 'cypress-root'
|
||||
|
||||
const isComponentSpec = () => Cypress.spec.specType === 'component'
|
||||
const isComponentSpec = () => true
|
||||
|
||||
function checkMountModeEnabled () {
|
||||
if (!isComponentSpec()) {
|
||||
@@ -46,7 +48,7 @@ const injectStyles = (options: MountOptions) => {
|
||||
})
|
||||
```
|
||||
**/
|
||||
export const mount = (jsx: React.ReactElement, options: MountOptions = {}) => {
|
||||
export const mount = (jsx: React.ReactNode, options: MountOptions = {}) => {
|
||||
checkMountModeEnabled()
|
||||
|
||||
// Get the display name property via the component constructor
|
||||
@@ -105,9 +107,10 @@ export const mount = (jsx: React.ReactElement, options: MountOptions = {}) => {
|
||||
|
||||
if (logInstance) {
|
||||
const logConsoleProps = {
|
||||
// @ts-ignore protect the use of jsx functional components use ReactNode
|
||||
props: jsx.props,
|
||||
description: 'Mounts React component',
|
||||
home: 'https://github.com/bahmutov/cypress-react-unit-test',
|
||||
home: 'https://github.com/cypress-io/cypress',
|
||||
}
|
||||
const componentElement = el.children[0]
|
||||
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
// "supportFile": "node_modules/@cypress/react/support"
|
||||
// adds commands from @cypress/react
|
||||
require('../dist/hooks')
|
||||
require('cypress-react-selector')
|
||||
require('@cypress/code-coverage/support')
|
||||
|
||||
6
npm/vue/babel.config.json
Normal file
6
npm/vue/babel.config.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-modules-commonjs",
|
||||
"babel-plugin-istanbul"
|
||||
]
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"viewportWidth": 500,
|
||||
"viewportHeight": 500,
|
||||
"video": false,
|
||||
"responseTimeout": 2500,
|
||||
"projectId": "134ej7",
|
||||
"testFiles": "**/*spec.js",
|
||||
"experimentalComponentTesting": true,
|
||||
|
||||
@@ -7,7 +7,8 @@ import { mount } from '@cypress/vue'
|
||||
import messages from './translations.json'
|
||||
|
||||
function expectHelloWorldGreeting () {
|
||||
cy.viewport(400, 200)
|
||||
// TODO: Support this API!
|
||||
// cy.viewport(400, 200)
|
||||
const allLocales = Cypress.vue.$i18n.availableLocales
|
||||
|
||||
// ensure we don't strip locales
|
||||
|
||||
@@ -4,7 +4,8 @@ import Users from './3-Users.vue'
|
||||
// test file can import the entire AxiosApi module
|
||||
import * as AxiosApi from './AxiosApi'
|
||||
|
||||
describe('Mocking imports from Axios Wrapper', () => {
|
||||
// TODO: esmodule mocking is broken
|
||||
xdescribe('Mocking imports from Axios Wrapper', () => {
|
||||
it('renders mocked data', () => {
|
||||
// stub export "get" that Users component imports and uses
|
||||
cy.stub(AxiosApi, 'get')
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as GreetingModule from './greeting'
|
||||
|
||||
describe('Mocking ES6 imports', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(300, 200)
|
||||
// cy.viewport(300, 200)
|
||||
})
|
||||
|
||||
it('shows real greeting without mocking', () => {
|
||||
|
||||
@@ -4,7 +4,8 @@ import { mount } from '@cypress/vue'
|
||||
|
||||
describe('Calculator', () => {
|
||||
it('adds two numbers', () => {
|
||||
cy.viewport(400, 200)
|
||||
// TODO: Uncomment with cy.viewport fixes are merged.
|
||||
// cy.viewport(400, 200)
|
||||
mount(Calculator)
|
||||
cy.get('[data-cy=a]').clear().type(23)
|
||||
cy.get('[data-cy=b]').clear().type(19)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import PizzaShop from './index'
|
||||
import Home from './Home'
|
||||
import Order from './Order'
|
||||
import PizzaShop from './index.vue'
|
||||
import Home from './Home.vue'
|
||||
import Order from './Order.vue'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
|
||||
@@ -6,59 +6,116 @@ import { mount } from '@cypress/vue'
|
||||
|
||||
/* eslint-env mocha */
|
||||
describe('AjaxList', () => {
|
||||
// because this component loads data right away
|
||||
// we need to setup XHR intercepts BEFORE mounting it
|
||||
// thus each test will first do its "cy.route"
|
||||
// then will mount the component
|
||||
context('using cy.intercept()', () => {
|
||||
// because this component loads data right away
|
||||
// we need to setup XHR intercepts BEFORE mounting it
|
||||
// thus each test will first do its "cy.route"
|
||||
// then will mount the component
|
||||
|
||||
it('loads list of posts', () => {
|
||||
mount(AjaxList)
|
||||
cy.get('li').should('have.length', 3)
|
||||
it('loads list of posts', () => {
|
||||
mount(AjaxList)
|
||||
cy.get('li').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('can inspect real data in XHR', () => {
|
||||
cy.intercept('/users?_limit=3').as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.wait('@users').its('response.body').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('can display mock XHR response', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.intercept('GET', '/users?_limit=3', { body: users }).as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.get('li').should('have.length', 1).first().contains('foo')
|
||||
})
|
||||
|
||||
it('can inspect mocked XHR', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.intercept('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.wait('@users').its('response.body').should('deep.equal', users)
|
||||
})
|
||||
|
||||
it('can delay and wait on XHR', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.intercept({
|
||||
method: 'GET',
|
||||
url: '/users?_limit=3',
|
||||
}, {
|
||||
delayMs: 1000,
|
||||
body: users,
|
||||
}).as('users')
|
||||
|
||||
mount(AjaxList)
|
||||
|
||||
cy.get('li').should('have.length', 0)
|
||||
cy.wait('@users')
|
||||
cy.get('li').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('can inspect real data in XHR', () => {
|
||||
cy.server()
|
||||
cy.route('/users?_limit=3').as('users')
|
||||
mount(AjaxList)
|
||||
context('using cy.route()', () => {
|
||||
// because this component loads data right away
|
||||
// we need to setup XHR intercepts BEFORE mounting it
|
||||
// thus each test will first do its "cy.route"
|
||||
// then will mount the component
|
||||
|
||||
cy.wait('@users').its('response.body').should('have.length', 3)
|
||||
})
|
||||
it('loads list of posts', () => {
|
||||
mount(AjaxList)
|
||||
cy.get('li').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('can display mock XHR response', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
it('can inspect real data in XHR', () => {
|
||||
cy.server()
|
||||
cy.route('/users?_limit=3').as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
cy.wait('@users').its('response.body').should('have.length', 3)
|
||||
})
|
||||
|
||||
cy.get('li').should('have.length', 1).first().contains('foo')
|
||||
})
|
||||
it('can display mock XHR response', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
it('can inspect mocked XHR', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
cy.get('li').should('have.length', 1).first().contains('foo')
|
||||
})
|
||||
|
||||
cy.wait('@users').its('response.body').should('deep.equal', users)
|
||||
})
|
||||
it('can inspect mocked XHR', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
it('can delay and wait on XHR', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.route({
|
||||
method: 'GET',
|
||||
url: '/users?_limit=3',
|
||||
response: users,
|
||||
delay: 1000,
|
||||
}).as('users')
|
||||
cy.wait('@users').its('response.body').should('deep.equal', users)
|
||||
})
|
||||
|
||||
mount(AjaxList)
|
||||
it('can delay and wait on XHR', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.get('li').should('have.length', 0)
|
||||
cy.wait('@users')
|
||||
cy.get('li').should('have.length', 1)
|
||||
cy.route({
|
||||
method: 'GET',
|
||||
url: '/users?_limit=3',
|
||||
response: users,
|
||||
delay: 1000,
|
||||
}).as('users')
|
||||
|
||||
mount(AjaxList)
|
||||
|
||||
cy.get('li').should('have.length', 0)
|
||||
cy.wait('@users')
|
||||
cy.get('li').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
/// <reference types="cypress" />
|
||||
const preprocessor = require('../../dist/plugins/webpack')
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
const webpackConfig = require('../../webpack.config')
|
||||
|
||||
/**
|
||||
* @type Cypress.PluginConfig
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
preprocessor(on, config, require('../../webpack.config.js'))
|
||||
require('@cypress/code-coverage/task')(on, config)
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const preprocessor = require('@cypress/vue/dist/plugins/webpack')
|
||||
const webpackConfig = require('@vue/cli-service/webpack.config')
|
||||
|
||||
module.exports = (on, config) => {
|
||||
preprocessor(on, config)
|
||||
preprocessor(on, config, webpackConfig)
|
||||
|
||||
// IMPORTANT return the config object
|
||||
return config
|
||||
|
||||
7969
npm/vue/examples/cli-ts/yarn.lock
Normal file
7969
npm/vue/examples/cli-ts/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
23
npm/vue/examples/cli2/.gitignore
vendored
Normal file
23
npm/vue/examples/cli2/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
24
npm/vue/examples/cli2/README.md
Normal file
24
npm/vue/examples/cli2/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# example
|
||||
|
||||
## Project setup
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
5
npm/vue/examples/cli2/babel.config.js
Normal file
5
npm/vue/examples/cli2/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset',
|
||||
],
|
||||
}
|
||||
1
npm/vue/examples/cli2/component-helpers.js
Normal file
1
npm/vue/examples/cli2/component-helpers.js
Normal file
@@ -0,0 +1 @@
|
||||
// setup code goes here
|
||||
7
npm/vue/examples/cli2/cypress.json
Normal file
7
npm/vue/examples/cli2/cypress.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"experimentalComponentTesting": true,
|
||||
"testFiles": "**/*.spec.*",
|
||||
"pluginsFile": "./plugins.js",
|
||||
"componentFolder": "./src",
|
||||
"componentSupportFile": "./component-helpers.js"
|
||||
}
|
||||
23
npm/vue/examples/cli2/package.json
Normal file
23
npm/vue/examples/cli2/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "vue-cli-2-example",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vue-cli-service build",
|
||||
"serve": "vue-cli-service serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^2.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
8
npm/vue/examples/cli2/plugins.js
Normal file
8
npm/vue/examples/cli2/plugins.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/// <reference types="cypress" />
|
||||
const preprocessor = require('../../dist/plugins/webpack')
|
||||
|
||||
module.exports = (on, config) => {
|
||||
preprocessor(on, config, require('@vue/cli-service/webpack.config'))
|
||||
|
||||
return config
|
||||
}
|
||||
BIN
npm/vue/examples/cli2/public/favicon.ico
Normal file
BIN
npm/vue/examples/cli2/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
17
npm/vue/examples/cli2/public/index.html
Normal file
17
npm/vue/examples/cli2/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
28
npm/vue/examples/cli2/src/App.vue
Normal file
28
npm/vue/examples/cli2/src/App.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<img alt="Vue logo" src="./assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
</style>
|
||||
BIN
npm/vue/examples/cli2/src/assets/logo.png
Normal file
BIN
npm/vue/examples/cli2/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
26
npm/vue/examples/cli2/src/components/HelloWorld.spec.js
Normal file
26
npm/vue/examples/cli2/src/components/HelloWorld.spec.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/* eslint-env mocha,chai,jest */
|
||||
|
||||
import HelloWorld from './HelloWorld'
|
||||
import { mount } from '@cypress/vue'
|
||||
|
||||
describe('hello', () => {
|
||||
it('works!', () => {
|
||||
mount(HelloWorld, {
|
||||
propsData: {
|
||||
msg: 'Hello World!',
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('h1').contains('Hello World!')
|
||||
})
|
||||
|
||||
it('works again', () => {
|
||||
mount(HelloWorld, {
|
||||
propsData: {
|
||||
msg: 'Hello World Again!',
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('h1').contains('Hello World Again!')
|
||||
})
|
||||
})
|
||||
62
npm/vue/examples/cli2/src/components/HelloWorld.vue
Normal file
62
npm/vue/examples/cli2/src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
* {
|
||||
color: red;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
26
npm/vue/examples/cli2/src/components/Prettiest.spec.js
Normal file
26
npm/vue/examples/cli2/src/components/Prettiest.spec.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/* eslint-env mocha,chai,jest */
|
||||
|
||||
import HelloWorld from './HelloWorld'
|
||||
import { mount } from '@cypress/vue'
|
||||
|
||||
describe('Prettiest', () => {
|
||||
it('spec works!', () => {
|
||||
mount(HelloWorld, {
|
||||
propsData: {
|
||||
msg: 'Hello World!',
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('h1').contains('Hello World!')
|
||||
})
|
||||
|
||||
it('spec works again', () => {
|
||||
mount(HelloWorld, {
|
||||
propsData: {
|
||||
msg: 'Hello World!',
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('h1').contains('Hello World!')
|
||||
})
|
||||
})
|
||||
16
npm/vue/examples/cli2/src/components/test/Pretty.spec.js
Normal file
16
npm/vue/examples/cli2/src/components/test/Pretty.spec.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* eslint-env mocha,chai,jest */
|
||||
|
||||
import HelloWorld from '../HelloWorld'
|
||||
import { mount } from '@cypress/vue'
|
||||
|
||||
describe('Pretty', () => {
|
||||
it('spec works', () => {
|
||||
mount(HelloWorld, {
|
||||
propsData: {
|
||||
msg: 'Hello World!',
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('h1').contains('Hello World!')
|
||||
})
|
||||
})
|
||||
8
npm/vue/examples/cli2/src/main.js
Normal file
8
npm/vue/examples/cli2/src/main.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
render: (h) => h(App),
|
||||
}).$mount('#app')
|
||||
7961
npm/vue/examples/cli2/yarn.lock
Normal file
7961
npm/vue/examples/cli2/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,11 +6,10 @@
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build-prod": "yarn build",
|
||||
"build:watch": "tsc --watch",
|
||||
"cy:open": "node ../../scripts/cypress open",
|
||||
"cy:run": "node ../../scripts/cypress run",
|
||||
"pretest": "yarn build",
|
||||
"test": "yarn cy:run"
|
||||
"cy:open": "node ../../scripts/start.js --component-testing --project ${PWD}",
|
||||
"cy:run": "node ../../scripts/cypress.js open-ct --run-project ${PWD}",
|
||||
"test": "yarn cy:run",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-modules-commonjs": "7.10.4",
|
||||
@@ -19,17 +18,19 @@
|
||||
"@intlify/vue-i18n-loader": "1.0.0",
|
||||
"@vue/test-utils": "1.0.3",
|
||||
"babel-plugin-istanbul": "6.0.0",
|
||||
"debug": "4.3.1",
|
||||
"debug": "4.3.2",
|
||||
"find-webpack": "2.1.0",
|
||||
"unfetch": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/preset-env": "7.9.5",
|
||||
"@vue/cli-plugin-babel": "~4.4.0",
|
||||
"@vue/cli-service": "~4.4.0",
|
||||
"axios": "0.19.2",
|
||||
"babel-loader": "8.1.0",
|
||||
"css-loader": "3.4.2",
|
||||
"cypress": "*",
|
||||
"cypress": "0.0.0-development",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"mocha": "7.1.1",
|
||||
"tailwindcss": "1.1.4",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/// <reference types="cypress" />
|
||||
import Vue from 'vue'
|
||||
import {
|
||||
createLocalVue,
|
||||
mount as testUtilsMount,
|
||||
@@ -14,8 +15,7 @@ const defaultOptions: (keyof MountOptions)[] = [
|
||||
]
|
||||
|
||||
function checkMountModeEnabled () {
|
||||
// @ts-ignore
|
||||
if (Cypress.spec.specType !== 'component') {
|
||||
if (!Cypress.spec.relative.includes('cypress/component')) {
|
||||
throw new Error(
|
||||
`In order to use mount or unmount functions please place the spec in component folder`,
|
||||
)
|
||||
@@ -70,17 +70,16 @@ const installMixins = (Vue, options) => {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const hasStore = ({ store }: { store: object }) => store && store._vm
|
||||
const hasStore = ({ store }: { store: any }) => store && store._vm // @ts-ignore
|
||||
|
||||
const forEachValue = (obj: object, fn: Function) => {
|
||||
const forEachValue = <T>(obj: Record<string, T>, fn: (value: T, key: string) => void) => {
|
||||
return Object.keys(obj).forEach((key) => fn(obj[key], key))
|
||||
}
|
||||
|
||||
const resetStoreVM = (Vue, { store }) => {
|
||||
// bind store public getters
|
||||
store.getters = {}
|
||||
const wrappedGetters = store._wrappedGetters
|
||||
const wrappedGetters = store._wrappedGetters as Record<string, (store: any) => void>
|
||||
const computed = {}
|
||||
|
||||
forEachValue(wrappedGetters, (fn, key) => {
|
||||
@@ -120,13 +119,13 @@ type VueComponent = Vue.ComponentOptions<any> | Vue.VueConstructor
|
||||
*
|
||||
* @interface ComponentOptions
|
||||
*/
|
||||
interface ComponentOptions {}
|
||||
type ComponentOptions = Record<string, unknown>
|
||||
|
||||
// local placeholder types
|
||||
type VueLocalComponents = object
|
||||
type VueLocalComponents = Record<string, VueComponent>
|
||||
|
||||
type VueFilters = {
|
||||
[key: string]: Function
|
||||
[key: string]: (value: string) => string
|
||||
}
|
||||
|
||||
type VueMixin = unknown
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// <reference types="cypress" />
|
||||
const { onFileDefaultPreprocessor } = require('../../preprocessor/webpack')
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
|
||||
/**
|
||||
* Registers Cypress preprocessor for Vue component testing.
|
||||
@@ -18,7 +18,7 @@ const { onFileDefaultPreprocessor } = require('../../preprocessor/webpack')
|
||||
*/
|
||||
const cypressPluginsFn = (on, config, webpackConfig) => {
|
||||
require('@cypress/code-coverage/task')(on, config)
|
||||
on('file:preprocessor', onFileDefaultPreprocessor(config, webpackConfig))
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const onFilePreprocessor = require('./webpack').onFilePreprocessor
|
||||
|
||||
module.exports = onFilePreprocessor
|
||||
@@ -1,197 +0,0 @@
|
||||
import webpack from 'webpack'
|
||||
import util from 'util'
|
||||
|
||||
// Cypress webpack bundler adaptor
|
||||
// https://github.com/cypress-io/cypress-webpack-preprocessor
|
||||
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
|
||||
const debug = require('debug')('@cypress/vue')
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
// const { VueLoaderPlugin } = require('vue-loader')
|
||||
const fw = require('find-webpack')
|
||||
|
||||
// Preventing chunks because we don't serve static assets
|
||||
function preventChunking (options = {}) {
|
||||
if (options && options.optimization && options.optimization.splitChunks) {
|
||||
delete options.optimization.splitChunks
|
||||
}
|
||||
|
||||
options.plugins = options.plugins || []
|
||||
options.plugins.push(
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1, // no chunks from dynamic imports -- includes the entry file
|
||||
}),
|
||||
)
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
// Base 64 all the things because we don't serve static assets
|
||||
function inlineUrlLoadedAssets (options = {}) {
|
||||
const isUrlLoader = (use) => {
|
||||
return use && use.loader && use.loader.indexOf('url-loader') > -1
|
||||
}
|
||||
const mergeUrlLoaderOptions = (use) => {
|
||||
if (isUrlLoader(use)) {
|
||||
use.options = use.options || {}
|
||||
use.options.limit = Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
return use
|
||||
}
|
||||
|
||||
if (options.module && options.module.rules) {
|
||||
options.module.rules = options.module.rules.map((rule) => {
|
||||
if (Array.isArray(rule.use)) {
|
||||
rule.use = rule.use.map(mergeUrlLoaderOptions)
|
||||
}
|
||||
|
||||
return rule
|
||||
})
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
function compileTemplate (options = {}) {
|
||||
options.resolve = options.resolve || {}
|
||||
options.resolve.alias = options.resolve.alias || {}
|
||||
options.resolve.alias['vue$'] = 'vue/dist/vue.esm.js'
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning: modifies the input object
|
||||
<<<<<<< HEAD
|
||||
* @param {WebpackOptions} options
|
||||
*/
|
||||
|
||||
function removeForkTsCheckerWebpackPlugin (options) {
|
||||
if (!Array.isArray(options.plugins)) {
|
||||
return
|
||||
}
|
||||
|
||||
options.plugins = options.plugins.filter((plugin) => {
|
||||
return plugin.typescript === undefined
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning: modifies the input object
|
||||
=======
|
||||
>>>>>>> origin
|
||||
* @param {Cypress.ConfigOptions} config
|
||||
* @param {WebpackOptions} options
|
||||
*/
|
||||
function insertBabelLoader (config, options) {
|
||||
const skipCodeCoverage = config && config.env && config.env.coverage === false
|
||||
|
||||
if (!options.devtool) {
|
||||
options.devtool = '#eval-source-map'
|
||||
}
|
||||
|
||||
const babelRule = {
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
options: {
|
||||
plugins: [
|
||||
// this plugin allows ES6 imports mocking
|
||||
[
|
||||
'@babel/plugin-transform-modules-commonjs',
|
||||
{
|
||||
loose: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
if (skipCodeCoverage) {
|
||||
debug('not adding code instrument plugin')
|
||||
} else {
|
||||
debug('adding code coverage plugin')
|
||||
// this plugin instruments the loaded code
|
||||
// which allows us to collect code coverage
|
||||
const instrumentPlugin = [
|
||||
'babel-plugin-istanbul',
|
||||
{
|
||||
// specify some options for NYC instrumentation here
|
||||
// like tell it to instrument both JavaScript and Vue files
|
||||
extension: ['.js', '.vue'],
|
||||
},
|
||||
]
|
||||
|
||||
babelRule.options.plugins.push(instrumentPlugin)
|
||||
}
|
||||
|
||||
options.module = options.module || {}
|
||||
options.module.rules = options.module.rules || []
|
||||
options.module.rules.push(babelRule)
|
||||
options.plugins = options.plugins || []
|
||||
|
||||
const pluginFound = options.plugins.find((plugin) => {
|
||||
return (
|
||||
plugin.constructor && plugin.constructor.name === VueLoaderPlugin.name
|
||||
)
|
||||
})
|
||||
|
||||
if (!pluginFound) {
|
||||
debug('inserting VueLoaderPlugin')
|
||||
options.plugins.push(new VueLoaderPlugin())
|
||||
} else {
|
||||
debug('found plugin VueLoaderPlugin already')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Cypress Vue Webpack file loader for .vue files.
|
||||
*/
|
||||
const onFileDefaultPreprocessor = (config, webpackOptions = fw.getWebpackOptions()) => {
|
||||
if (!webpackOptions) {
|
||||
debug('Could not find webpack options, starting with default')
|
||||
webpackOptions = {}
|
||||
}
|
||||
|
||||
webpackOptions.mode = 'development'
|
||||
|
||||
inlineUrlLoadedAssets(webpackOptions)
|
||||
preventChunking(webpackOptions)
|
||||
compileTemplate(webpackOptions)
|
||||
insertBabelLoader(config, webpackOptions)
|
||||
|
||||
// if I remove it, then get another message
|
||||
// [VueLoaderPlugin Error] No matching use for vue-loader is found.
|
||||
// removeForkTsCheckerWebpackPlugin(webpackOptions)
|
||||
|
||||
if (debug.enabled) {
|
||||
console.error('final webpack')
|
||||
console.error(util.inspect(webpackOptions, false, 2, true))
|
||||
}
|
||||
|
||||
return webpackPreprocessor({
|
||||
webpackOptions,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Vue loader from the client projects that already have `webpack.config.js`
|
||||
*
|
||||
* @example
|
||||
* const {
|
||||
* onFilePreprocessor
|
||||
* } = require('@cypress/vue/preprocessor/webpack')
|
||||
* module.exports = on => {
|
||||
* on('file:preprocessor', onFilePreprocessor('../path/to/webpack.config'))
|
||||
* }
|
||||
*/
|
||||
const onFilePreprocessor = (webpackOptions) => {
|
||||
if (typeof webpackOptions === 'string') {
|
||||
// load webpack config from the given path
|
||||
webpackOptions = require(webpackOptions)
|
||||
}
|
||||
|
||||
return webpackPreprocessor({
|
||||
webpackOptions,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { onFilePreprocessor, onFileDefaultPreprocessor }
|
||||
@@ -2,14 +2,24 @@
|
||||
// The default for running tests in this project
|
||||
// https://vue-loader.vuejs.org/guide/#manual-setup
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'js/[name].js',
|
||||
publicPath: '/',
|
||||
chunkFilename: 'js/[name].js',
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js', '.json', '.vue'],
|
||||
alias: {
|
||||
// point at the built file
|
||||
'@cypress/vue': path.join(__dirname, 'dist'),
|
||||
vue: 'vue/dist/vue.esm.js',
|
||||
},
|
||||
},
|
||||
module: {
|
||||
@@ -18,6 +28,10 @@ module.exports = {
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
// this will apply to both plain `.css` files
|
||||
// AND `<style>` blocks in `.vue` files
|
||||
{
|
||||
|
||||
12
npm/webpack-dev-server/README.md
Normal file
12
npm/webpack-dev-server/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Webpack-ct
|
||||
|
||||
> **Note** this package is not meant to be used outside of cypress component testing.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
- 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
|
||||
14
npm/webpack-dev-server/circle.yml
Normal file
14
npm/webpack-dev-server/circle.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
node: circleci/node@1.1.6
|
||||
jobs:
|
||||
build:
|
||||
executor:
|
||||
name: node/default
|
||||
tag: '12'
|
||||
steps:
|
||||
- checkout
|
||||
- node/with-cache:
|
||||
steps:
|
||||
- run: yarn
|
||||
- run: yarn test
|
||||
12
npm/webpack-dev-server/index-template.html
Normal file
12
npm/webpack-dev-server/index-template.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
1
npm/webpack-dev-server/index.js
Normal file
1
npm/webpack-dev-server/index.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('./dist')
|
||||
35
npm/webpack-dev-server/package.json
Normal file
35
npm/webpack-dev-server/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@cypress/webpack-dev-server",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Launches Webpack Dev Server for Component Testing",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build-prod": "tsc",
|
||||
"test": "tsc && ts-mocha --config ./test/.mocharc.js --exit",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "4.3.2",
|
||||
"semver": "^7.3.4",
|
||||
"webpack-merge": "^5.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/webpack-dev-server": "^3.11.1",
|
||||
"chai": "^4.2.0",
|
||||
"mocha": "^8.1.3",
|
||||
"ts-mocha": "8.0.0",
|
||||
"typescript": "^3.9.7",
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"html-webpack-plugin": "> 3",
|
||||
"webpack": "> 4"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
36
npm/webpack-dev-server/src/aut-runner.ts
Normal file
36
npm/webpack-dev-server/src/aut-runner.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/*eslint-env browser,mocha*/
|
||||
|
||||
function appendTargetIfNotExists (id: string, tag = 'div', parent = document.body) {
|
||||
let node = document.getElementById(id)
|
||||
|
||||
if (!node) {
|
||||
node = document.createElement(tag)
|
||||
node.setAttribute('id', id)
|
||||
parent.appendChild(node)
|
||||
}
|
||||
|
||||
node.innerHTML = ''
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
export function init (importPromises, parent = (window.opener || window.parent)) {
|
||||
const Cypress = (window as any).Cypress = parent.Cypress
|
||||
|
||||
if (!Cypress) {
|
||||
throw new Error('Tests cannot run without a reference to Cypress!')
|
||||
}
|
||||
|
||||
Cypress.onSpecWindow(window, importPromises)
|
||||
Cypress.action('app:window:before:load', window)
|
||||
|
||||
beforeEach(() => {
|
||||
const root = appendTargetIfNotExists('__cy_root')
|
||||
|
||||
root.appendChild(appendTargetIfNotExists('__cy_app'))
|
||||
})
|
||||
|
||||
return {
|
||||
restartRunner: Cypress.restartRunner,
|
||||
}
|
||||
}
|
||||
5
npm/webpack-dev-server/src/browser.ts
Normal file
5
npm/webpack-dev-server/src/browser.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
function render () {
|
||||
require('!!./loader.js!./browser.js')
|
||||
}
|
||||
|
||||
render()
|
||||
42
npm/webpack-dev-server/src/index.ts
Normal file
42
npm/webpack-dev-server/src/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { debug as debugFn } from 'debug'
|
||||
import { AddressInfo } from 'net'
|
||||
import { Server } from 'http'
|
||||
import { start as createDevServer } from './startServer'
|
||||
const debug = debugFn('cypress:webpack-dev-server:webpack')
|
||||
|
||||
export interface DevServerOptions {
|
||||
specs: Cypress.Cypress['spec'][]
|
||||
config: {
|
||||
supportFile: string
|
||||
projectRoot: string
|
||||
webpackDevServerPublicPathRoute: string
|
||||
}
|
||||
devServerEvents: EventEmitter
|
||||
}
|
||||
|
||||
export interface StartDevServer {
|
||||
/* this is the Cypress options object */
|
||||
options: DevServerOptions
|
||||
/* support passing a path to the user's webpack config */
|
||||
webpackConfig?: Record<string, any>
|
||||
}
|
||||
|
||||
export interface ResolvedDevServerConfig {
|
||||
port: number
|
||||
server: Server
|
||||
}
|
||||
|
||||
export async function startDevServer (startDevServerArgs: StartDevServer) {
|
||||
const webpackDevServer = await createDevServer(startDevServerArgs)
|
||||
|
||||
return new Promise<ResolvedDevServerConfig>((resolve) => {
|
||||
const httpSvr = webpackDevServer.listen(0, '127.0.0.1', () => {
|
||||
// FIXME: handle address returning a string
|
||||
const port = (httpSvr.address() as AddressInfo).port
|
||||
|
||||
debug('Component testing webpack server started on port', port)
|
||||
resolve({ port, server: httpSvr })
|
||||
})
|
||||
})
|
||||
}
|
||||
69
npm/webpack-dev-server/src/loader.ts
Normal file
69
npm/webpack-dev-server/src/loader.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/* global Cypress */
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import * as path from 'path'
|
||||
import { CypressCTWebpackContext } from './plugin'
|
||||
|
||||
/**
|
||||
* @param {ComponentSpec} file spec to create import string from.
|
||||
* @param {string} filename name of the spec file - this is the same as file.name
|
||||
* @param {string} chunkName webpack chunk name. eg: 'spec-0'
|
||||
* @param {string} projectRoot absolute path to the project root. eg: /Users/<username>/my-app
|
||||
*/
|
||||
const makeImport = (file: Cypress.Cypress['spec'], filename: string, chunkName: string, projectRoot: string) => {
|
||||
// If we want to rename the chunks, we can use this
|
||||
const magicComments = chunkName ? `/* webpackChunkName: "${chunkName}" */` : ''
|
||||
|
||||
return `"${filename}": {
|
||||
shouldLoad: () => document.location.pathname.includes(${JSON.stringify(file.relative)}),
|
||||
load: () => import(${JSON.stringify(path.resolve(projectRoot, file.relative))} ${magicComments}),
|
||||
chunkName: "${chunkName}",
|
||||
}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a object maping a spec file to an object mapping
|
||||
* the spec name to the result of `makeImport`.
|
||||
*
|
||||
* @returns {Record<string, ReturnType<makeImport>}
|
||||
* {
|
||||
* "App.spec.js": {
|
||||
* shouldLoad: () => document.location.pathname.includes("cypress/component/App.spec.js"),
|
||||
* load: () => {
|
||||
* return import("/Users/projects/my-app/cypress/component/App.spec.js" \/* webpackChunkName: "spec-0" *\/)
|
||||
* },
|
||||
* chunkName: "spec-0"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
function buildSpecs (projectRoot: string, files: Cypress.Cypress['spec'][] = []): string {
|
||||
if (!Array.isArray(files)) return `{}`
|
||||
|
||||
return `{${files.map((f, i) => {
|
||||
return makeImport(f, f.name, `spec-${i}`, projectRoot)
|
||||
}).join(',')}}`
|
||||
}
|
||||
|
||||
// Runs the tests inside the iframe
|
||||
export default function loader (this: CypressCTWebpackContext) {
|
||||
const { files, projectRoot, supportFile } = this._cypress
|
||||
|
||||
const supportFileAbsolutePath = supportFile ? JSON.stringify(path.resolve(projectRoot, supportFile)) : undefined
|
||||
|
||||
return `
|
||||
var loadSupportFile = ${supportFile ? `() => import(${supportFileAbsolutePath})` : `() => Promise.resolve()`}
|
||||
var allTheSpecs = ${buildSpecs(projectRoot, files)};
|
||||
|
||||
var { init } = require(${JSON.stringify(require.resolve('./aut-runner'))})
|
||||
|
||||
var scriptLoaders = Object.values(allTheSpecs).reduce(
|
||||
(accSpecLoaders, specLoader) => {
|
||||
if (specLoader.shouldLoad()) {
|
||||
accSpecLoaders.push(specLoader.load)
|
||||
}
|
||||
return accSpecLoaders
|
||||
}, [loadSupportFile])
|
||||
|
||||
init(scriptLoaders)
|
||||
`
|
||||
}
|
||||
55
npm/webpack-dev-server/src/makeWebpackConfig.ts
Normal file
55
npm/webpack-dev-server/src/makeWebpackConfig.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { debug as debugFn } from 'debug'
|
||||
import * as path from 'path'
|
||||
import { Configuration } from 'webpack'
|
||||
import { merge } from 'webpack-merge'
|
||||
import CypressCTOptionsPlugin, { CypressCTOptionsPluginOptions } from './plugin'
|
||||
|
||||
const debug = debugFn('cypress:webpack-dev-server:makeWebpackConfig')
|
||||
|
||||
const mergePublicPath = (baseValue, userValue = '/') => {
|
||||
return path.join(baseValue, userValue, '/')
|
||||
}
|
||||
|
||||
interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions {
|
||||
webpackDevServerPublicPathRoute: string
|
||||
}
|
||||
|
||||
export async function makeWebpackConfig (userWebpackConfig: Configuration, options: MakeWebpackConfigOptions): Promise<Configuration> {
|
||||
const { projectRoot, webpackDevServerPublicPathRoute, files, supportFile, devServerEvents } = options
|
||||
|
||||
debug(`User passed in webpack config with values %o`, userWebpackConfig)
|
||||
|
||||
const defaultWebpackConfig = require('./webpack.config')
|
||||
|
||||
debug(`Merging Evergreen's webpack config with users'`)
|
||||
|
||||
debug(`New webpack entries %o`, files)
|
||||
debug(`Project root`, projectRoot)
|
||||
debug(`Support file`, supportFile)
|
||||
|
||||
const entry = path.resolve(__dirname, './browser.js')
|
||||
|
||||
const publicPath = mergePublicPath(webpackDevServerPublicPathRoute, userWebpackConfig?.output?.publicPath)
|
||||
|
||||
const dynamicWebpackConfig = {
|
||||
output: {
|
||||
publicPath,
|
||||
},
|
||||
plugins: [
|
||||
new CypressCTOptionsPlugin({
|
||||
files,
|
||||
projectRoot,
|
||||
devServerEvents,
|
||||
supportFile,
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
const mergedConfig = merge<Configuration>(userWebpackConfig, defaultWebpackConfig, dynamicWebpackConfig)
|
||||
|
||||
mergedConfig.entry = entry
|
||||
|
||||
debug('Merged webpack config %o', mergedConfig)
|
||||
|
||||
return mergedConfig
|
||||
}
|
||||
109
npm/webpack-dev-server/src/plugin.ts
Normal file
109
npm/webpack-dev-server/src/plugin.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import webpack, { Compiler, compilation, Plugin } from 'webpack'
|
||||
import { EventEmitter } from 'events'
|
||||
import _ from 'lodash'
|
||||
import semver from 'semver'
|
||||
import fs, { PathLike } from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
type UtimesSync = (path: PathLike, atime: string | number | Date, mtime: string | number | Date) => void
|
||||
|
||||
export interface CypressCTOptionsPluginOptions {
|
||||
files: Cypress.Cypress['spec'][]
|
||||
projectRoot: string
|
||||
supportFile: string
|
||||
devServerEvents?: EventEmitter
|
||||
}
|
||||
|
||||
export interface CypressCTWebpackContext extends compilation.Compilation {
|
||||
_cypress: CypressCTOptionsPluginOptions
|
||||
}
|
||||
|
||||
export default class CypressCTOptionsPlugin implements Plugin {
|
||||
private files: Cypress.Cypress['spec'][] = []
|
||||
private supportFile: string
|
||||
private errorEmitted = false
|
||||
|
||||
private readonly projectRoot: string
|
||||
private readonly devServerEvents: EventEmitter
|
||||
|
||||
constructor (options: CypressCTOptionsPluginOptions) {
|
||||
this.files = options.files
|
||||
this.supportFile = options.supportFile
|
||||
this.projectRoot = options.projectRoot
|
||||
this.devServerEvents = options.devServerEvents
|
||||
}
|
||||
|
||||
private pluginFunc = (context: CypressCTWebpackContext, module: compilation.Module) => {
|
||||
context._cypress = {
|
||||
files: this.files,
|
||||
projectRoot: this.projectRoot,
|
||||
supportFile: this.supportFile,
|
||||
}
|
||||
};
|
||||
|
||||
private setupCustomHMR = (compiler: webpack.Compiler) => {
|
||||
compiler.hooks.afterCompile.tap(
|
||||
'CypressCTOptionsPlugin',
|
||||
(compilation: compilation.Compilation) => {
|
||||
const stats = compilation.getStats()
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
this.errorEmitted = true
|
||||
this.devServerEvents.emit('dev-server:compile:error', stats.toJson().errors[0])
|
||||
} else if (this.errorEmitted) {
|
||||
// compilation succeed but assets haven't emitted to the output yet
|
||||
this.devServerEvents.emit('dev-server:compile:error', null)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
compiler.hooks.afterEmit.tap(
|
||||
'CypressCTOptionsPlugin',
|
||||
(compilation: compilation.Compilation) => {
|
||||
if (!compilation.getStats().hasErrors()) {
|
||||
this.devServerEvents.emit('dev-server:compile:success')
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param compilation webpack 4 `compilation.Compilation`, webpack 5
|
||||
* `Compilation`
|
||||
*/
|
||||
private plugin = (compilation: compilation.Compilation) => {
|
||||
this.devServerEvents.on('dev-server:specs:changed', (specs) => {
|
||||
if (_.isEqual(specs, this.files)) return
|
||||
|
||||
this.files = specs
|
||||
const inputFileSystem = compilation.inputFileSystem
|
||||
const utimesSync: UtimesSync = semver.gt('4.0.0', webpack.version) ? inputFileSystem.fileSystem.utimesSync : fs.utimesSync
|
||||
|
||||
utimesSync(path.resolve(__dirname, 'browser.js'), new Date(), new Date())
|
||||
})
|
||||
|
||||
// Webpack 5
|
||||
/* istanbul ignore next */
|
||||
if ('NormalModule' in webpack) {
|
||||
// @ts-ignore
|
||||
webpack.NormalModule.getCompilationHooks(compilation).loader.tap(
|
||||
'CypressCTOptionsPlugin',
|
||||
this.pluginFunc,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Webpack 4
|
||||
compilation.hooks.normalModuleLoader.tap(
|
||||
'CypressCTOptionsPlugin',
|
||||
this.pluginFunc,
|
||||
)
|
||||
};
|
||||
|
||||
apply (compiler: Compiler): void {
|
||||
this.setupCustomHMR(compiler)
|
||||
compiler.hooks.compilation.tap('CypressCTOptionsPlugin', this.plugin)
|
||||
}
|
||||
}
|
||||
50
npm/webpack-dev-server/src/startServer.ts
Normal file
50
npm/webpack-dev-server/src/startServer.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import Debug from 'debug'
|
||||
import webpack from 'webpack'
|
||||
import WebpackDevServer from 'webpack-dev-server'
|
||||
import { StartDevServer } from '.'
|
||||
import { makeWebpackConfig } from './makeWebpackConfig'
|
||||
|
||||
const debug = Debug('cypress:webpack-dev-server:start')
|
||||
|
||||
export async function start ({ webpackConfig: userWebpackConfig, options }: StartDevServer): Promise<WebpackDevServer> {
|
||||
if (!userWebpackConfig) {
|
||||
debug('User did not pass in any webpack configuration')
|
||||
}
|
||||
|
||||
const { projectRoot, webpackDevServerPublicPathRoute } = options.config
|
||||
|
||||
const webpackConfig = await makeWebpackConfig(userWebpackConfig || {}, {
|
||||
files: options.specs,
|
||||
projectRoot,
|
||||
webpackDevServerPublicPathRoute,
|
||||
devServerEvents: options.devServerEvents,
|
||||
supportFile: options.config.supportFile,
|
||||
})
|
||||
|
||||
debug('compiling webpack')
|
||||
|
||||
const compiler = webpack(webpackConfig)
|
||||
|
||||
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,
|
||||
inline: false,
|
||||
}
|
||||
|
||||
return new WebpackDevServer(compiler, webpackDevServerConfig)
|
||||
}
|
||||
18
npm/webpack-dev-server/src/webpack.config.js
Normal file
18
npm/webpack-dev-server/src/webpack.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const path = require('path')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
plugins: [new HtmlWebpackPlugin({
|
||||
template: path.join(__dirname, '../index-template.html'),
|
||||
})],
|
||||
}
|
||||
3
npm/webpack-dev-server/test/.mocharc.js
Normal file
3
npm/webpack-dev-server/test/.mocharc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
spec: 'test/**/*.spec.ts',
|
||||
}
|
||||
145
npm/webpack-dev-server/test/e2e.spec.ts
Normal file
145
npm/webpack-dev-server/test/e2e.spec.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import webpack from 'webpack'
|
||||
import path from 'path'
|
||||
import { expect } from 'chai'
|
||||
import { EventEmitter } from 'events'
|
||||
import http from 'http'
|
||||
import fs from 'fs'
|
||||
|
||||
import { startDevServer } from '../'
|
||||
|
||||
const requestSpecFile = (port: number) => {
|
||||
return new Promise((res) => {
|
||||
const opts = {
|
||||
host: 'localhost',
|
||||
port,
|
||||
path: '/test/fixtures/foo.spec.js',
|
||||
}
|
||||
|
||||
const callback = (response: EventEmitter) => {
|
||||
let str = ''
|
||||
|
||||
response.on('data', (chunk) => {
|
||||
str += chunk
|
||||
})
|
||||
|
||||
response.on('end', () => {
|
||||
res(str)
|
||||
})
|
||||
}
|
||||
|
||||
http.request(opts, callback).end()
|
||||
})
|
||||
}
|
||||
|
||||
const root = path.join(__dirname, '..')
|
||||
|
||||
const webpackConfig: webpack.Configuration = {
|
||||
output: {
|
||||
path: root,
|
||||
publicPath: root,
|
||||
},
|
||||
}
|
||||
|
||||
const specs: Cypress.Cypress['spec'][] = [
|
||||
{
|
||||
name: `${root}/test/fixtures/foo.spec.js`,
|
||||
relative: `${root}/test/fixtures/foo.spec.js`,
|
||||
absolute: `${root}/test/fixtures/foo.spec.js`,
|
||||
},
|
||||
]
|
||||
|
||||
const config = {
|
||||
projectRoot: root,
|
||||
webpackDevServerPublicPathRoute: root,
|
||||
}
|
||||
|
||||
describe('#startDevServer', () => {
|
||||
it('serves specs via a webpack dev server', async () => {
|
||||
const { port, server } = await startDevServer({
|
||||
webpackConfig,
|
||||
options: {
|
||||
config,
|
||||
specs,
|
||||
devServerEvents: new EventEmitter(),
|
||||
},
|
||||
})
|
||||
|
||||
const response = await requestSpecFile(port as number)
|
||||
|
||||
expect(response).to.eq('const foo = () => {}\n')
|
||||
|
||||
return new Promise((res) => {
|
||||
server.close(() => res())
|
||||
})
|
||||
})
|
||||
|
||||
it('emits dev-server:compile:success event on successful compilation', async () => {
|
||||
const devServerEvents = new EventEmitter()
|
||||
const { server } = await startDevServer({
|
||||
webpackConfig,
|
||||
options: {
|
||||
config,
|
||||
specs,
|
||||
devServerEvents,
|
||||
},
|
||||
})
|
||||
|
||||
return new Promise((res) => {
|
||||
devServerEvents.on('dev-server:compile:success', () => {
|
||||
server.close(() => res())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('emits dev-server:compile:error event on error compilation', async () => {
|
||||
const devServerEvents = new EventEmitter()
|
||||
const { server } = await startDevServer({
|
||||
webpackConfig,
|
||||
options: {
|
||||
config,
|
||||
specs: [
|
||||
{
|
||||
name: `${root}/test/fixtures/compilation-fails.spec.js`,
|
||||
relative: `${root}/test/fixtures/compilation-fails.spec.js`,
|
||||
absolute: `${root}/test/fixtures/compilation-fails.spec.js`,
|
||||
},
|
||||
],
|
||||
devServerEvents,
|
||||
},
|
||||
})
|
||||
|
||||
return new Promise((res) => {
|
||||
devServerEvents.on('dev-server:compile:error', () => {
|
||||
server.close(() => res())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('touches browser.js when a spec file is added', async function () {
|
||||
const devServerEvents = new EventEmitter()
|
||||
const { server } = await startDevServer({
|
||||
webpackConfig,
|
||||
options: {
|
||||
config,
|
||||
specs,
|
||||
devServerEvents,
|
||||
},
|
||||
})
|
||||
|
||||
const newSpec: Cypress.Cypress['spec'] = {
|
||||
name: './some-newly-created-spec.js',
|
||||
relative: './some-newly-created-spec.js',
|
||||
absolute: '/some-newly-created-spec.js',
|
||||
}
|
||||
|
||||
const oldmtime = fs.statSync('./dist/browser.js').mtimeMs
|
||||
|
||||
return new Promise((res) => {
|
||||
devServerEvents.emit('dev-server:specs:changed', [newSpec])
|
||||
const updatedmtime = fs.statSync('./dist/browser.js').mtimeMs
|
||||
|
||||
expect(oldmtime).to.not.equal(updatedmtime)
|
||||
server.close(() => res())
|
||||
})
|
||||
})
|
||||
})
|
||||
1
npm/webpack-dev-server/test/fixtures/compilation-fails.spec.js
vendored
Normal file
1
npm/webpack-dev-server/test/fixtures/compilation-fails.spec.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
this is an invalid spec file
|
||||
1
npm/webpack-dev-server/test/fixtures/foo.spec.js
vendored
Normal file
1
npm/webpack-dev-server/test/fixtures/foo.spec.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
const foo = () => {}
|
||||
53
npm/webpack-dev-server/tsconfig.json
Normal file
53
npm/webpack-dev-server/tsconfig.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
|
||||
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
] /* Specify library files to be included in the compilation: */,
|
||||
"allowJs": true /* Allow javascript files to be compiled. */,
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "dist" /* Redirect output structure to the directory. */,
|
||||
// "rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": false /* Enable all strict type-checking options. */,
|
||||
"noImplicitAny": false,
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [] /* Type declaration files to be included in compilation. */,
|
||||
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["*.js"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -6,7 +6,7 @@ exports['webpack preprocessor - e2e correctly preprocesses the file 1'] = `
|
||||
exports['webpack preprocessor - e2e has less verbose syntax error 1'] = `
|
||||
Webpack Compilation Error
|
||||
.<path>/_test-output/syntax_error_spec.js
|
||||
Module build failed (from /[root]/node_modules/babel-loader/lib/index.js):
|
||||
Module build failed (from ./node_modules/babel-loader/lib/index.js):
|
||||
SyntaxError: <path>/_test-output/syntax_error_spec.js: Unexpected token (1:18)
|
||||
|
||||
[0m[31m[1m>[22m[39m[90m 1 | [39mdescribe([32m'fail'[39m[33m,[39m [33m-[39m[33m>[39m)[0m
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bluebird": "^3.7.1",
|
||||
"debug": "^4.1.1",
|
||||
"debug": "4.3.2",
|
||||
"lodash": "^4.17.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -13,9 +13,7 @@ const { expect } = chai
|
||||
const preprocessor = require('../../dist/index')
|
||||
|
||||
const normalizeErrMessage = (message) => {
|
||||
return message
|
||||
.replace(/\/\S+\/_test/g, '<path>/_test')
|
||||
.split(path.resolve(process.cwd(), '../..')).join('/[root]')
|
||||
return message.replace(/\/\S+\/_test/g, '<path>/_test')
|
||||
}
|
||||
|
||||
const fixturesDir = path.join(__dirname, '..', 'fixtures')
|
||||
|
||||
21
package.json
21
package.json
@@ -57,7 +57,7 @@
|
||||
"test-scripts": "mocha -r packages/ts/register --reporter spec 'scripts/unit/**/*spec.js'",
|
||||
"test-scripts-watch": "yarn test-scripts --watch --watch-extensions 'ts,js'",
|
||||
"pretest-unit": "yarn ensure-deps",
|
||||
"test-unit": "lerna exec yarn test-unit --ignore \"'@packages/{desktop-gui,driver,root,static,web-config}'\"",
|
||||
"test-unit": "lerna exec yarn test-unit --ignore \"'{@packages/{desktop-gui,driver,root,static,web-config,net-stubbing,rewriter,ui-components},@cypress/{webpack-dev-server,eslint-plugin-dev}}'\"",
|
||||
"pretest-watch": "yarn ensure-deps",
|
||||
"test-watch": "lerna exec yarn test-watch --ignore \"'@packages/{desktop-gui,driver,root,static,web-config}'\"",
|
||||
"type-check": "node scripts/type_check",
|
||||
@@ -86,7 +86,6 @@
|
||||
"@types/chai-enzyme": "0.6.7",
|
||||
"@types/classnames": "2.2.9",
|
||||
"@types/debug": "4.1.5",
|
||||
"@types/enzyme": "3.9.1",
|
||||
"@types/enzyme-adapter-react-16": "1.0.5",
|
||||
"@types/execa": "0.9.0",
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
@@ -102,8 +101,8 @@
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@types/request-promise": "4.1.45",
|
||||
"@types/sinon-chai": "3.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "2.14.0",
|
||||
"@typescript-eslint/parser": "2.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "3",
|
||||
"@typescript-eslint/parser": "3",
|
||||
"ansi-styles": "3.2.1",
|
||||
"arg": "4.1.2",
|
||||
"ascii-table": "0.0.9",
|
||||
@@ -116,9 +115,10 @@
|
||||
"chalk": "2.4.2",
|
||||
"check-dependencies": "1.1.0",
|
||||
"check-more-types": "2.24.0",
|
||||
"commander": "6.2.1",
|
||||
"common-tags": "1.8.0",
|
||||
"conventional-recommended-bump": "6.1.0",
|
||||
"debug": "4.3.1",
|
||||
"debug": "4.3.2",
|
||||
"del": "3.0.0",
|
||||
"electron-builder": "22.9.1",
|
||||
"electron-notarize": "1.0.0",
|
||||
@@ -134,6 +134,7 @@
|
||||
"find-package-json": "1.2.0",
|
||||
"fs-extra": "8.1.0",
|
||||
"gift": "0.10.2",
|
||||
"glob": "7.1.6",
|
||||
"globby": "10.0.1",
|
||||
"got": "11.5.1",
|
||||
"gulp": "4.0.2",
|
||||
@@ -157,6 +158,7 @@
|
||||
"listr": "0.14.3",
|
||||
"lodash": "4.17.19",
|
||||
"make-empty-github-commit": "1.2.0",
|
||||
"minimist": "1.2.5",
|
||||
"mocha": "3.5.3",
|
||||
"mocha-banner": "1.1.2",
|
||||
"mocha-junit-reporter": "2.0.0",
|
||||
@@ -227,7 +229,9 @@
|
||||
],
|
||||
"nohoist": [
|
||||
"**/@ffmpeg-installer",
|
||||
"**/@ffmpeg-installer/**"
|
||||
"**/@ffmpeg-installer/**",
|
||||
"**/webpack-preprocessor/babel-loader",
|
||||
"**/webpack-batteries-included-preprocessor/ts-loader"
|
||||
]
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -241,8 +245,11 @@
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"**/@types/cheerio": "0.22.21",
|
||||
"**/@types/enzyme": "3.10.5",
|
||||
"**/@types/react": "16.9.50",
|
||||
"**/jquery": "3.1.1",
|
||||
"**/pretty-format": "26.4.0"
|
||||
"**/pretty-format": "26.4.0",
|
||||
"**/socket.io-parser": "4.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('src/cypress/script_utils', () => {
|
||||
|
||||
context('#runPromises', () => {
|
||||
it('handles promises and doesnt try to fetch + eval manually', async () => {
|
||||
const scriptsAsPromises = [Promise.resolve(), Promise.resolve()]
|
||||
const scriptsAsPromises = [() => Promise.resolve(), () => Promise.resolve()]
|
||||
const result = await $scriptUtils.runScripts({}, scriptsAsPromises)
|
||||
|
||||
expect(result).to.have.length(scriptsAsPromises.length)
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"compression": "1.7.4",
|
||||
"cors": "2.8.5",
|
||||
"cypress-multi-reporters": "1.4.0",
|
||||
"debug": "4.3.1",
|
||||
"debug": "4.3.2",
|
||||
"error-stack-parser": "2.0.6",
|
||||
"errorhandler": "1.5.1",
|
||||
"eventemitter2": "6.4.2",
|
||||
|
||||
@@ -352,6 +352,7 @@ module.exports = (Commands, Cypress, cy, state, config) => {
|
||||
|
||||
Cypress.on('test:before:run:async', () => {
|
||||
// reset any state on the backend
|
||||
// TODO: this is a bug in e2e it needs to be returned
|
||||
return Cypress.backend('reset:server:state')
|
||||
})
|
||||
|
||||
|
||||
@@ -169,6 +169,21 @@ class $Cypress {
|
||||
return this.runner.run(fn)
|
||||
}
|
||||
|
||||
// Method to manually re-execute Runner (usually within $autIframe)
|
||||
// used mainly by Component Testing
|
||||
restartRunner () {
|
||||
if (!window.top.Cypress) {
|
||||
throw Error('Cannot re-run spec without Cypress')
|
||||
}
|
||||
|
||||
// MobX state is only available on the Runner instance
|
||||
// which is attached to the top level `window`
|
||||
// We avoid infinite restart loop by checking if not in a loading state.
|
||||
if (!window.top.Runner.state.isLoading) {
|
||||
window.top.Runner.emit('restart')
|
||||
}
|
||||
}
|
||||
|
||||
// onSpecWindow is called as the spec window
|
||||
// is being served but BEFORE any of the actual
|
||||
// specs or support files have been downloaded
|
||||
|
||||
@@ -38,9 +38,12 @@ const runScriptsFromUrls = (specWindow, scripts) => {
|
||||
// Supports either scripts as objects or as async import functions
|
||||
const runScripts = (specWindow, scripts) => {
|
||||
// if scripts contains at least one promise
|
||||
if (scripts.length && typeof scripts[0].then === 'function') {
|
||||
// merge the awaiting of the promises
|
||||
return Bluebird.all(scripts)
|
||||
if (scripts.length && typeof scripts[0] === 'function') {
|
||||
// chain the loading promises
|
||||
// NOTE: since in evalScripts, scripts are evaluated in order,
|
||||
// we chose to respect this constraint here too.
|
||||
// indeed _.each goes through the array in order
|
||||
return Bluebird.each(scripts, (script) => script())
|
||||
}
|
||||
|
||||
return runScriptsFromUrls(specWindow, scripts)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"dependencies": {
|
||||
"@cypress/icons": "0.7.0",
|
||||
"bluebird": "3.5.3",
|
||||
"debug": "4.3.1",
|
||||
"debug": "4.3.2",
|
||||
"electron-packager": "14.1.1",
|
||||
"fs-extra": "8.1.0",
|
||||
"lodash": "4.17.19",
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
const { client, circularParser } = require('@packages/socket/lib/browser')
|
||||
|
||||
const connect = (host, path, extraOpts = {}) => {
|
||||
return client.connect(host, {
|
||||
path,
|
||||
transports: ['websocket'],
|
||||
// @ts-ignore
|
||||
parser: circularParser,
|
||||
...extraOpts,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
connect,
|
||||
|
||||
socketIoClient: client,
|
||||
|
||||
socketIoParser: circularParser,
|
||||
}
|
||||
9
packages/extension/app/client.ts
Normal file
9
packages/extension/app/client.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { client } from '@packages/socket/lib/browser'
|
||||
|
||||
export const connect = (host, path, extraOpts = {}) => {
|
||||
return client.io(host, {
|
||||
path,
|
||||
transports: ['websocket'],
|
||||
...extraOpts,
|
||||
})
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
const fs = require('fs-extra')
|
||||
const pkg = require('./package.json')
|
||||
const gulp = require('gulp')
|
||||
const rimraf = require('rimraf')
|
||||
const source = require('vinyl-source-stream')
|
||||
const browserify = require('browserify')
|
||||
const cypressIcons = require('@cypress/icons')
|
||||
import fs from 'fs-extra'
|
||||
import gulp from 'gulp'
|
||||
import rimraf from 'rimraf'
|
||||
import webpack from 'webpack'
|
||||
import cypressIcons from '@cypress/icons'
|
||||
import webpackConfig from './webpack.config.js'
|
||||
|
||||
const copySocketClient = () => {
|
||||
return gulp.src(require('../socket').getPathToClientSource())
|
||||
.pipe(gulp.dest('dist'))
|
||||
}
|
||||
const pkg = require('./package.json')
|
||||
|
||||
const clean = (done) => {
|
||||
rimraf('dist', done)
|
||||
@@ -29,13 +25,10 @@ const manifest = (done) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const background = () => {
|
||||
return browserify({
|
||||
entries: 'app/init.js',
|
||||
})
|
||||
.bundle()
|
||||
.pipe(source('background.js'))
|
||||
.pipe(gulp.dest('dist'))
|
||||
const background = (cb) => {
|
||||
const compiler = webpack(webpackConfig as webpack.Configuration)
|
||||
|
||||
compiler.run(cb)
|
||||
}
|
||||
|
||||
const html = () => {
|
||||
@@ -69,7 +62,6 @@ const logos = () => {
|
||||
const build = gulp.series(
|
||||
clean,
|
||||
gulp.parallel(
|
||||
copySocketClient,
|
||||
icons,
|
||||
logos,
|
||||
manifest,
|
||||
@@ -11,9 +11,9 @@
|
||||
"postinstall": "echo '@packages/extension needs: yarn build'",
|
||||
"test": "yarn test-unit",
|
||||
"test-debug": "yarn test-unit --inspect-brk=5566",
|
||||
"test-unit": "cross-env NODE_ENV=test mocha --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json",
|
||||
"test-unit": "cross-env NODE_ENV=test mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json",
|
||||
"test-watch": "yarn test-unit --watch",
|
||||
"watch": "gulp watch"
|
||||
"watch": "webpack --watch --progress"
|
||||
},
|
||||
"dependencies": {
|
||||
"bluebird": "3.5.3",
|
||||
@@ -22,7 +22,6 @@
|
||||
"devDependencies": {
|
||||
"@cypress/icons": "0.7.0",
|
||||
"@packages/socket": "0.0.0-development",
|
||||
"browserify": "16.3.0",
|
||||
"chai": "3.5.0",
|
||||
"coffeescript": "1.12.7",
|
||||
"cross-env": "6.0.3",
|
||||
@@ -36,8 +35,9 @@
|
||||
"rimraf": "3.0.2",
|
||||
"sinon": "7.3.2",
|
||||
"sinon-chai": "3.3.0",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
"webextension-polyfill": "0.4.0"
|
||||
"ts-loader": "8.0.13",
|
||||
"webextension-polyfill": "0.4.0",
|
||||
"webpack": "4.44.2"
|
||||
},
|
||||
"files": [
|
||||
"app",
|
||||
|
||||
12
packages/extension/tsconfig.json
Normal file
12
packages/extension/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"importHelpers": true,
|
||||
"strict": false
|
||||
}
|
||||
}
|
||||
22
packages/extension/webpack.config.js
Normal file
22
packages/extension/webpack.config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
mode: process.env.NODE_ENV || 'development',
|
||||
entry: './app/init.js',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
output: {
|
||||
filename: 'background.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user