mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-04 22:30:00 -06:00
chore: deliver npm modules (#15398)
* fix: deliver webpack-dev-server new architecture * fix: make vite-dev-server work * fix: deliver react and vite-dev-server * fix: deliver the vue2 package too * fix: rollback react changes published to next
This commit is contained in:
committed by
GitHub
parent
261b0b97b4
commit
5fd91bde01
5
npm/vite-dev-server/cypress/components/Foo.tsx
Normal file
5
npm/vite-dev-server/cypress/components/Foo.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react'
|
||||
|
||||
export const Foo: React.FC = () => {
|
||||
return <div>Hello world!!!!</div>
|
||||
}
|
||||
13
npm/vite-dev-server/cypress/components/react-no-jsx.spec.jsx
Normal file
13
npm/vite-dev-server/cypress/components/react-no-jsx.spec.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import { mount } from '@cypress/react'
|
||||
|
||||
const Comp = () => {
|
||||
return <div>Hello world!</div>
|
||||
}
|
||||
|
||||
describe('React', () => {
|
||||
it('renders a react component', () => {
|
||||
mount(<Comp />)
|
||||
cy.get('div').contains('Hello world')
|
||||
})
|
||||
})
|
||||
13
npm/vite-dev-server/cypress/components/react-no-jsx.spec.ts
Normal file
13
npm/vite-dev-server/cypress/components/react-no-jsx.spec.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import { mount } from '@cypress/react'
|
||||
|
||||
const Comp = () => {
|
||||
return React.createElement('div', {}, 'Hello world')
|
||||
}
|
||||
|
||||
describe('React', () => {
|
||||
it('renders a react component', () => {
|
||||
mount(React.createElement(Comp))
|
||||
cy.get('div').contains('Hello world')
|
||||
})
|
||||
})
|
||||
25
npm/vite-dev-server/cypress/components/react-no-jsx.spec.tsx
Normal file
25
npm/vite-dev-server/cypress/components/react-no-jsx.spec.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react'
|
||||
import { mount } from '@cypress/react'
|
||||
import { Foo } from './Foo'
|
||||
|
||||
describe('React', () => {
|
||||
it('renders a react component #1', () => {
|
||||
mount(<Foo />)
|
||||
cy.get('div').contains('Hello world')
|
||||
})
|
||||
|
||||
it('renders a react component #2', () => {
|
||||
mount(<Foo />)
|
||||
cy.get('div').contains('Hello world')
|
||||
})
|
||||
|
||||
it('renders a react component #3', () => {
|
||||
mount(<Foo />)
|
||||
cy.get('div').contains('Hello world')
|
||||
})
|
||||
|
||||
it('renders a react component #4', () => {
|
||||
mount(<Foo />)
|
||||
cy.get('div').contains('Hello world')
|
||||
})
|
||||
})
|
||||
5
npm/vite-dev-server/cypress/fixtures/example.json
Normal file
5
npm/vite-dev-server/cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import { startDevServer } from '@cypress/vite-dev-server'
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('dev-server:start', async (options) => {
|
||||
return startDevServer({ options })
|
||||
})
|
||||
on('dev-server:start', async (options) => startDevServer({ options }))
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -24,7 +24,27 @@
|
||||
return node
|
||||
}
|
||||
|
||||
let importsToLoad = [() => import("{{{specPath}}}")];
|
||||
const specPath = "{{{specPath}}}"
|
||||
|
||||
let importsToLoad = [() => import(specPath).catch(e => {
|
||||
// if the import failed, it might be because of dependencies
|
||||
// so we try a quick refresh just in case it is
|
||||
|
||||
// Since vite does not work with IE we can use URLSearchParams without polyfill
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const r = searchParams.has("refresh") ? parseInt(searchParams.get("refresh"), 10) + 1 : 0
|
||||
// limit the number of refresh (dependency discovery depths)
|
||||
// to 2 instead of 1 for React-DOM
|
||||
if (r < 2) {
|
||||
searchParams.set('refresh', r)
|
||||
window.location.search = searchParams
|
||||
} else {
|
||||
throw new Error(`
|
||||
**Error during compilation.**
|
||||
Check the terminal log for more info
|
||||
`, e)
|
||||
}
|
||||
})];
|
||||
if ("{{{supportPath}}}") {
|
||||
importsToLoad.push(() => import("{{{supportPath}}}"));
|
||||
}
|
||||
@@ -38,7 +58,7 @@
|
||||
Cypress.onSpecWindow(window, importsToLoad)
|
||||
Cypress.action('app:window:before:load', window)
|
||||
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
const root = appendTargetIfNotExists('__cy_root')
|
||||
|
||||
root.appendChild(appendTargetIfNotExists('__cy_app'))
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mustache": "4.1.1",
|
||||
"vite": "2.0.1"
|
||||
"vite": "2.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">= 2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"index-template.html"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -34,5 +35,8 @@
|
||||
"bugs": "https://github.com/cypress-io/cypress/issues/new?template=1-bug-report.md",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
},
|
||||
"ciJobs": [
|
||||
"npm-vite-dev-server"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ export const makeCypressPlugin = (
|
||||
server.middlewares.use('/index.html', (req, res) => handleIndex(indexHtml, projectRoot, supportFilePath, req, res))
|
||||
},
|
||||
handleHotUpdate: () => {
|
||||
console.log('HOT UPDATE')
|
||||
devServerEvents.emit('dev-server:compile:success')
|
||||
|
||||
return []
|
||||
|
||||
1
npm/vue/.gitignore
vendored
Normal file
1
npm/vue/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
cypress/videos
|
||||
@@ -4,8 +4,8 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vue-cli-service build",
|
||||
"cy:open": "../../node_modules/.bin/cypress open",
|
||||
"cy:run": "../../node_modules/.bin/cypress run",
|
||||
"cy:open": "node ../../../../scripts/cypress open",
|
||||
"cy:run": "node ../../../../scripts/cypress run",
|
||||
"serve": "vue-cli-service serve"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
"name": "@cypress/vue",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Browser-based Component Testing for Vue.js with Cypress.io ✌️🌲",
|
||||
"main": "dist/index.js",
|
||||
"main": "dist/cypress-vue.cjs.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "rimraf dist && yarn rollup -c rollup.config.js",
|
||||
"build-prod": "yarn build",
|
||||
"cy:open": "node ../../scripts/cypress.js open-ct --project ${PWD}",
|
||||
"cy:run": "node ../../scripts/cypress.js run-ct --project ${PWD}",
|
||||
"test": "yarn cy:run",
|
||||
"watch": "tsc -w"
|
||||
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cypress/code-coverage": "3.8.1",
|
||||
"@cypress/webpack-dev-server": "0.0.0-development",
|
||||
"@vue/test-utils": "1.0.3",
|
||||
"unfetch": "4.1.0"
|
||||
},
|
||||
@@ -21,7 +19,11 @@
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.10.4",
|
||||
"@babel/preset-env": "7.9.5",
|
||||
"@cypress/code-coverage": "3.8.1",
|
||||
"@cypress/webpack-dev-server": "0.0.0-development",
|
||||
"@intlify/vue-i18n-loader": "1.0.0",
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@vue/cli-plugin-babel": "~4.4.0",
|
||||
"@vue/cli-service": "~4.4.0",
|
||||
"axios": "0.19.2",
|
||||
@@ -33,6 +35,9 @@
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"find-webpack": "2.1.0",
|
||||
"mocha": "7.1.1",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-istanbul": "2.0.1",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"tailwindcss": "1.1.4",
|
||||
"typescript": "3.9.6",
|
||||
"vue": "2.6.11",
|
||||
@@ -45,12 +50,14 @@
|
||||
"webpack": "4.42.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cypress/webpack-dev-server": "*",
|
||||
"babel-loader": "8",
|
||||
"cypress": ">=4.5.0",
|
||||
"vue": "2.x"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
"dist/**/*",
|
||||
"src/**/*.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -68,6 +75,13 @@
|
||||
"cypress",
|
||||
"vue"
|
||||
],
|
||||
"unpkg": "dist/cypress-vue.browser.js",
|
||||
"module": "dist/cypress-vue.esm-bundler.js",
|
||||
"peerDependenciesMeta": {
|
||||
"@cypress/webpack-dev-server": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "http://registry.npmjs.org/"
|
||||
|
||||
86
npm/vue/rollup.config.js
Normal file
86
npm/vue/rollup.config.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import ts from 'rollup-plugin-typescript2'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
* ${pkg.name} v${pkg.version}
|
||||
* (c) ${new Date().getFullYear()} Cypress.io
|
||||
* Released under the MIT License
|
||||
*/
|
||||
`
|
||||
|
||||
function createEntry (options) {
|
||||
const {
|
||||
format,
|
||||
input,
|
||||
isBrowser,
|
||||
} = options
|
||||
|
||||
const config = {
|
||||
input,
|
||||
external: [
|
||||
'vue',
|
||||
'@vue/test-utils',
|
||||
'@cypress/webpack-dev-server',
|
||||
],
|
||||
plugins: [
|
||||
resolve({ preferBuiltins: true }), commonjs(),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
name: 'CypressVue',
|
||||
file: pkg.unpkg,
|
||||
format,
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
'@vue/test-utils': 'VueTestUtils',
|
||||
},
|
||||
exports: 'auto',
|
||||
},
|
||||
}
|
||||
|
||||
if (input === 'src/index.ts') {
|
||||
if (format === 'es') {
|
||||
config.output.file = pkg.module
|
||||
if (isBrowser) {
|
||||
config.output.file = pkg.unpkg
|
||||
}
|
||||
}
|
||||
|
||||
if (format === 'cjs') {
|
||||
config.output.file = pkg.main
|
||||
}
|
||||
} else {
|
||||
config.output.file = input.replace(/^src\//, 'dist/')
|
||||
}
|
||||
|
||||
console.log(`Building ${format}: ${config.output.file}`)
|
||||
|
||||
config.plugins.push(
|
||||
ts({
|
||||
check: format === 'es' && isBrowser,
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: format === 'es',
|
||||
target: 'es5', // not sure what this should be?
|
||||
module: format === 'cjs' ? 'es2015' : 'esnext',
|
||||
},
|
||||
exclude: ['tests'],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export default [
|
||||
createEntry({ format: 'es', input: 'src/index.ts', isBrowser: false }),
|
||||
createEntry({ format: 'es', input: 'src/index.ts', isBrowser: true }),
|
||||
createEntry({ format: 'iife', input: 'src/index.ts', isBrowser: true }),
|
||||
createEntry({ format: 'cjs', input: 'src/index.ts', isBrowser: false }),
|
||||
createEntry({ format: 'cjs', input: 'src/support.js', isBrowser: false }),
|
||||
createEntry({ format: 'cjs', input: 'src/plugins/webpack/index.js', isBrowser: false }),
|
||||
]
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
VueTestUtilsConfigOptions,
|
||||
Wrapper,
|
||||
} from '@vue/test-utils'
|
||||
import { renderTestingPlatform, ROOT_ID } from './renderTestingPlatform'
|
||||
|
||||
const defaultOptions: (keyof MountOptions)[] = [
|
||||
'vue',
|
||||
@@ -357,15 +358,11 @@ export const mount = (
|
||||
const document: Document = cy.state('document')
|
||||
|
||||
document.body.innerHTML = ''
|
||||
let el = document.getElementById('cypress-jsdom')
|
||||
let el = document.getElementById(ROOT_ID)
|
||||
|
||||
// If the target div doesn't exist, create it
|
||||
if (!el) {
|
||||
const div = document.createElement('div')
|
||||
|
||||
div.id = 'cypress-jsdom'
|
||||
document.body.appendChild(div)
|
||||
el = div
|
||||
el = renderTestingPlatform(document.head.innerHTML)
|
||||
}
|
||||
|
||||
if (typeof options.stylesheets === 'string') {
|
||||
|
||||
@@ -17,7 +17,6 @@ const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
* }
|
||||
*/
|
||||
const cypressPluginsFn = (on, config, webpackConfig) => {
|
||||
require('@cypress/code-coverage/task')(on, config)
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))
|
||||
|
||||
return config
|
||||
|
||||
20
npm/vue/src/renderTestingPlatform.ts
Normal file
20
npm/vue/src/renderTestingPlatform.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export const ROOT_ID = '__cy_root'
|
||||
|
||||
/** Initialize an empty document with root element
|
||||
* This only needs for experimentalComponentTesting
|
||||
*/
|
||||
export function renderTestingPlatform (headInnerHTML: string) {
|
||||
// @ts-expect-error no idea
|
||||
const document = cy.state('document')
|
||||
|
||||
if (document.body) document.body.innerHTML = ''
|
||||
|
||||
if (document.head) document.head.innerHTML = headInnerHTML
|
||||
|
||||
const rootNode = document.createElement('div')
|
||||
|
||||
rootNode.setAttribute('id', ROOT_ID)
|
||||
document.getElementsByTagName('body')[0].prepend(rootNode)
|
||||
|
||||
return rootNode
|
||||
}
|
||||
@@ -1,47 +1,11 @@
|
||||
/* eslint-env mocha */
|
||||
import unfetch from 'unfetch'
|
||||
|
||||
require('@cypress/code-coverage/support')
|
||||
const { renderTestingPlatform } = require('./renderTestingPlatform')
|
||||
|
||||
let headInnerHTML = document.head.innerHTML
|
||||
|
||||
/** Initialize an empty document with root element */
|
||||
function renderTestingPlatform () {
|
||||
const document = cy.state('document')
|
||||
|
||||
if (document.body) document.body.innerHTML = ''
|
||||
|
||||
if (document.head) document.head.innerHTML = headInnerHTML
|
||||
|
||||
const rootNode = document.createElement('div')
|
||||
|
||||
rootNode.setAttribute('id', 'cypress-jsdom')
|
||||
document.getElementsByTagName('body')[0].prepend(rootNode)
|
||||
|
||||
return cy.get('#cypress-jsdom', { log: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces window.fetch with a polyfill based on XMLHttpRequest
|
||||
* that Cypress can spy on and stub
|
||||
* @see https://www.cypress.io/blog/2020/06/29/experimental-fetch-polyfill/
|
||||
*/
|
||||
function polyfillFetchIfNeeded () {
|
||||
// @ts-ignore
|
||||
if (Cypress.config('experimentalFetchPolyfill')) {
|
||||
// @ts-ignore
|
||||
if (!cy.state('fetchPolyfilled')) {
|
||||
delete window.fetch
|
||||
window.fetch = unfetch
|
||||
// @ts-ignore
|
||||
cy.state('fetchPolyfilled', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
renderTestingPlatform()
|
||||
polyfillFetchIfNeeded()
|
||||
renderTestingPlatform(headInnerHTML)
|
||||
})
|
||||
|
||||
before(() => {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
/* 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. */
|
||||
"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. */
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const path = require('path')
|
||||
const pkg = require('package.json')
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
@@ -18,7 +19,7 @@ module.exports = {
|
||||
extensions: ['.js', '.json', '.vue'],
|
||||
alias: {
|
||||
// point at the built file
|
||||
'@cypress/vue': path.join(__dirname, 'dist'),
|
||||
'@cypress/vue': path.join(__dirname, pkg.main),
|
||||
vue: 'vue/dist/vue.esm.js',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,6 +7,5 @@
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "4.3.2",
|
||||
"lazy-compile-webpack-plugin": "0.1.11",
|
||||
"semver": "^7.3.4",
|
||||
"webpack-merge": "^5.4.0"
|
||||
},
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
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)
|
||||
if (node) {
|
||||
// it is required to completely remove node from the document
|
||||
// cause framework can store the information between renders inside the root node (like react-dom is doing)
|
||||
node.parentElement.removeChild(node)
|
||||
}
|
||||
|
||||
node.innerHTML = ''
|
||||
node = document.createElement(tag)
|
||||
node.setAttribute('id', id)
|
||||
parent.appendChild(node)
|
||||
|
||||
return node
|
||||
}
|
||||
@@ -24,10 +26,12 @@ export function init (importPromises, parent = (window.opener || window.parent))
|
||||
Cypress.onSpecWindow(window, importPromises)
|
||||
Cypress.action('app:window:before:load', window)
|
||||
|
||||
beforeEach(() => {
|
||||
const root = appendTargetIfNotExists('__cy_root')
|
||||
|
||||
root.appendChild(appendTargetIfNotExists('__cy_app'))
|
||||
// Before all tests we are mounting the root element, **not beforeEach**
|
||||
// Cleaning up platform between tests is the responsibility of the specific adapter
|
||||
// because unmounting react/vue component should be done using specific framework API
|
||||
// (for devtools and to get rid of global event listeners from previous tests.)
|
||||
before(() => {
|
||||
appendTargetIfNotExists('__cy_root')
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { debug as debugFn } from 'debug'
|
||||
import { AddressInfo } from 'net'
|
||||
import { start as createDevServer } from './startServer'
|
||||
const debug = debugFn('cypress:webpack-dev-server:webpack')
|
||||
import { UserWebpackDevServerOptions } from './makeWebpackConfig'
|
||||
|
||||
export interface DevServerOptions {
|
||||
specs: Cypress.Cypress['spec'][]
|
||||
config: {
|
||||
supportFile: string
|
||||
projectRoot: string
|
||||
webpackDevServerPublicPathRoute: string
|
||||
}
|
||||
devServerEvents: EventEmitter
|
||||
}
|
||||
const debug = debugFn('cypress:webpack-dev-server:webpack')
|
||||
|
||||
type DoneCallback = () => unknown
|
||||
|
||||
@@ -21,15 +12,15 @@ export interface ResolvedDevServerConfig {
|
||||
close: (done?: DoneCallback) => void
|
||||
}
|
||||
|
||||
export interface StartDevServer {
|
||||
export interface StartDevServer extends UserWebpackDevServerOptions {
|
||||
/* this is the Cypress options object */
|
||||
options: DevServerOptions
|
||||
options: Cypress.DevServerOptions
|
||||
/* support passing a path to the user's webpack config */
|
||||
webpackConfig?: Record<string, any>
|
||||
}
|
||||
|
||||
export async function startDevServer (startDevServerArgs: StartDevServer) {
|
||||
const webpackDevServer = await createDevServer(startDevServerArgs)
|
||||
export async function startDevServer (startDevServerArgs: StartDevServer, exitProcess = process.exit) {
|
||||
const webpackDevServer = await createDevServer(startDevServerArgs, exitProcess)
|
||||
|
||||
return new Promise<ResolvedDevServerConfig>((resolve) => {
|
||||
const httpSvr = webpackDevServer.listen(0, '127.0.0.1', () => {
|
||||
|
||||
@@ -1,28 +1,51 @@
|
||||
import { debug as debugFn } from 'debug'
|
||||
import * as path from 'path'
|
||||
import { Configuration } from 'webpack'
|
||||
import * as webpack from 'webpack'
|
||||
import { merge } from 'webpack-merge'
|
||||
import defaultWebpackConfig from './webpack.config'
|
||||
import LazyCompilePlugin from 'lazy-compile-webpack-plugin'
|
||||
import CypressCTOptionsPlugin, { CypressCTOptionsPluginOptions } from './plugin'
|
||||
|
||||
const debug = debugFn('cypress:webpack-dev-server:makeWebpackConfig')
|
||||
const WEBPACK_MAJOR_VERSION = Number(webpack.version.split('.')[0])
|
||||
|
||||
export interface UserWebpackDevServerOptions {
|
||||
/**
|
||||
* if `true` will compile all the specs together when the first one is request and can slow up initial build time.
|
||||
* @default false
|
||||
*/
|
||||
disableLazyCompilation?: boolean
|
||||
}
|
||||
|
||||
interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions, UserWebpackDevServerOptions {
|
||||
webpackDevServerPublicPathRoute: string
|
||||
isOpenMode: boolean
|
||||
}
|
||||
|
||||
const mergePublicPath = (baseValue, userValue = '/') => {
|
||||
return path.join(baseValue, userValue, '/')
|
||||
}
|
||||
|
||||
interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions {
|
||||
webpackDevServerPublicPathRoute: string
|
||||
function getLazyCompilationWebpackConfig (options: MakeWebpackConfigOptions): webpack.Configuration {
|
||||
if (options.disableLazyCompilation || !options.isOpenMode) {
|
||||
return {}
|
||||
}
|
||||
|
||||
switch (WEBPACK_MAJOR_VERSION) {
|
||||
case 4:
|
||||
return { plugins: [new LazyCompilePlugin()] }
|
||||
case 5:
|
||||
return { experiments: { lazyCompilation: true } } as webpack.Configuration
|
||||
default:
|
||||
return { }
|
||||
}
|
||||
}
|
||||
|
||||
export async function makeWebpackConfig (userWebpackConfig: Configuration, options: MakeWebpackConfigOptions): Promise<Configuration> {
|
||||
export async function makeWebpackConfig (userWebpackConfig: webpack.Configuration, options: MakeWebpackConfigOptions): Promise<webpack.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)
|
||||
@@ -45,7 +68,12 @@ export async function makeWebpackConfig (userWebpackConfig: Configuration, optio
|
||||
],
|
||||
}
|
||||
|
||||
const mergedConfig = merge<Configuration>(userWebpackConfig, defaultWebpackConfig, dynamicWebpackConfig)
|
||||
const mergedConfig = merge<webpack.Configuration>(
|
||||
userWebpackConfig,
|
||||
defaultWebpackConfig,
|
||||
dynamicWebpackConfig,
|
||||
getLazyCompilationWebpackConfig(options),
|
||||
)
|
||||
|
||||
mergedConfig.entry = entry
|
||||
|
||||
|
||||
@@ -6,25 +6,38 @@ import { makeWebpackConfig } from './makeWebpackConfig'
|
||||
|
||||
const debug = Debug('cypress:webpack-dev-server:start')
|
||||
|
||||
export async function start ({ webpackConfig: userWebpackConfig, options }: StartDevServer): Promise<WebpackDevServer> {
|
||||
export async function start ({ webpackConfig: userWebpackConfig, options, ...userOptions }: StartDevServer, exitProcess = process.exit): Promise<WebpackDevServer> {
|
||||
if (!userWebpackConfig) {
|
||||
debug('User did not pass in any webpack configuration')
|
||||
}
|
||||
|
||||
const { projectRoot, webpackDevServerPublicPathRoute } = options.config
|
||||
// @ts-expect-error ?? webpackDevServerPublicPathRoute is not a valid option of Cypress.Config
|
||||
const { projectRoot, webpackDevServerPublicPathRoute, isTextTerminal } = options.config
|
||||
|
||||
const webpackConfig = await makeWebpackConfig(userWebpackConfig || {}, {
|
||||
files: options.specs,
|
||||
projectRoot,
|
||||
webpackDevServerPublicPathRoute,
|
||||
devServerEvents: options.devServerEvents,
|
||||
supportFile: options.config.supportFile,
|
||||
supportFile: options.config.supportFile as string,
|
||||
isOpenMode: !isTextTerminal,
|
||||
...userOptions,
|
||||
})
|
||||
|
||||
debug('compiling webpack')
|
||||
|
||||
const compiler = webpack(webpackConfig)
|
||||
|
||||
// When compiling in run mode
|
||||
// Stop the clock early, no need to run all the tests on a failed build
|
||||
if (isTextTerminal) {
|
||||
compiler.hooks.done.tap('cyCustomErrorBuild', function (stats) {
|
||||
if (stats.hasErrors()) {
|
||||
exitProcess(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
debug('starting webpack dev server')
|
||||
|
||||
// TODO: write a test for how we are NOT modifying publicPath
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const path = require('path')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
/** @type {import('webpack').Configuration} */
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
optimization: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import webpack from 'webpack'
|
||||
import path from 'path'
|
||||
import sinon from 'sinon'
|
||||
import { expect } from 'chai'
|
||||
import { EventEmitter } from 'events'
|
||||
import http from 'http'
|
||||
@@ -51,8 +52,9 @@ const specs: Cypress.Cypress['spec'][] = [
|
||||
const config = {
|
||||
projectRoot: root,
|
||||
supportFile: '',
|
||||
isTextTerminal: true,
|
||||
webpackDevServerPublicPathRoute: root,
|
||||
}
|
||||
} as any as Cypress.ResolvedConfigOptions & Cypress.RuntimeConfigOptions
|
||||
|
||||
describe('#startDevServer', () => {
|
||||
it('serves specs via a webpack dev server', async () => {
|
||||
@@ -94,6 +96,9 @@ describe('#startDevServer', () => {
|
||||
|
||||
it('emits dev-server:compile:error event on error compilation', async () => {
|
||||
const devServerEvents = new EventEmitter()
|
||||
|
||||
const exitSpy = sinon.stub()
|
||||
|
||||
const { close } = await startDevServer({
|
||||
webpackConfig,
|
||||
options: {
|
||||
@@ -107,10 +112,13 @@ describe('#startDevServer', () => {
|
||||
],
|
||||
devServerEvents,
|
||||
},
|
||||
})
|
||||
}, exitSpy as any)
|
||||
|
||||
exitSpy()
|
||||
|
||||
return new Promise((res) => {
|
||||
devServerEvents.on('dev-server:compile:error', () => {
|
||||
expect(exitSpy.calledOnce).to.be.true
|
||||
close(() => res())
|
||||
})
|
||||
})
|
||||
|
||||
28
yarn.lock
28
yarn.lock
@@ -13421,7 +13421,7 @@ detect-node@^2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
|
||||
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
|
||||
|
||||
detect-port-alt@1.1.6:
|
||||
detect-port-alt@1.1.6, detect-port-alt@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
|
||||
integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==
|
||||
@@ -14577,10 +14577,10 @@ es6-weak-map@^2.0.1:
|
||||
es6-iterator "^2.0.3"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
esbuild@^0.8.34:
|
||||
version "0.8.38"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.38.tgz#04dc395e15c77bbc9d6798e9b31275546bcf7b9a"
|
||||
integrity sha512-wSunJl8ujgBs9eVGubc8Y6fn/DkDjNyfQBVOFTY1E7sRxr8KTjmqyLIiE0M3Z4CjMnCu/rttCugwnOzY+HiwIw==
|
||||
esbuild@^0.8.52:
|
||||
version "0.8.57"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.57.tgz#a42d02bc2b57c70bcd0ef897fe244766bb6dd926"
|
||||
integrity sha512-j02SFrUwFTRUqiY0Kjplwjm1psuzO1d6AjaXKuOR9hrY0HuPsT6sV42B6myW34h1q4CRy+Y3g4RU/cGJeI/nNA==
|
||||
|
||||
escalade@^3.0.1, escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
@@ -21463,6 +21463,14 @@ lazy-cache@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
|
||||
integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4=
|
||||
|
||||
lazy-compile-webpack-plugin@0.1.11:
|
||||
version "0.1.11"
|
||||
resolved "https://registry.yarnpkg.com/lazy-compile-webpack-plugin/-/lazy-compile-webpack-plugin-0.1.11.tgz#be3b9487ccc731a606dc55bcfcd80000c72e4237"
|
||||
integrity sha512-d2D72x0XqFSj83SRmgx1dvgRvmyIoXpC2lMj+XVS+xzt0FxXDPzF/2FbxOVmW9gzp6d8U29Ne4RGF4x+MTYwow==
|
||||
dependencies:
|
||||
detect-port-alt "^1.1.6"
|
||||
loader-utils "^1.2.3"
|
||||
|
||||
lazy-property@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lazy-property/-/lazy-property-1.0.0.tgz#84ddc4b370679ba8bd4cdcfa4c06b43d57111147"
|
||||
@@ -34259,12 +34267,12 @@ vinyl@^2.0.0, vinyl@^2.1.0, vinyl@^2.2.0:
|
||||
remove-trailing-separator "^1.0.1"
|
||||
replace-ext "^1.0.0"
|
||||
|
||||
vite@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.0.1.tgz#348fc5c0de510aa90bd01ecf87df210ce741b38e"
|
||||
integrity sha512-x7ZfikjNs+6n4cdvwb9L5r5xBCdjmtmHFHaI4JVR3nAkJbMCK/dynfDWky8/NseZ9Ncz1jVxTQ/Bcf+n1ps1Ww==
|
||||
vite@2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.0.5.tgz#ac46857a3fa8686d077921e61bd48a986931df1d"
|
||||
integrity sha512-QTgEDbq1WsTtr6j+++ewjhBFEk6c8v0xz4fb/OWJQKNYU8ZZtphOshwOqAlnarSstPBtWCBR0tsugXx6ajfoUg==
|
||||
dependencies:
|
||||
esbuild "^0.8.34"
|
||||
esbuild "^0.8.52"
|
||||
postcss "^8.2.1"
|
||||
resolve "^1.19.0"
|
||||
rollup "^2.38.5"
|
||||
|
||||
Reference in New Issue
Block a user