Skip to content

feat: Disable file watching in component tests in run mode #24097

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion npm/vite-dev-server/src/resolveConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type { Vite } from './getVite'

const debug = debugFn('cypress:vite-dev-server:resolve-config')

export const createViteDevServerConfig = async (config: ViteDevServerConfig, vite: Vite) => {
export const createViteDevServerConfig = async (config: ViteDevServerConfig, vite: Vite): Promise<InlineConfig> => {
const { specs, cypressConfig, viteConfig: viteOverrides } = config
const root = cypressConfig.projectRoot
const { default: findUp } = await importModule('find-up')
Expand Down Expand Up @@ -90,6 +90,17 @@ export const createViteDevServerConfig = async (config: ViteDevServerConfig, vit
].filter((p) => p != null),
}

if (config.cypressConfig.isTextTerminal) {
viteBaseConfig.server = {
...(viteBaseConfig.server || {}),
// Disable file watching and HMR when executing tests in `run` mode
watch: {
ignored: '**/*',
},
hmr: false,
}
}

let resolvedOverrides: UserConfig = {}

if (typeof viteOverrides === 'function') {
Expand Down
26 changes: 26 additions & 0 deletions npm/vite-dev-server/test/resolveConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,30 @@ describe('resolveConfig', function () {
})
})
})

describe('file watching', () => {
let viteDevServerConfig: ViteDevServerConfig

beforeEach(async () => {
const projectRoot = await scaffoldSystemTestProject('vite-inspect')

viteDevServerConfig = getViteDevServerConfig(projectRoot)
})

it('should be disabled in run mode', async () => {
viteDevServerConfig.cypressConfig.isTextTerminal = true
const viteConfig = await createViteDevServerConfig(viteDevServerConfig, vite)

expect(viteConfig.server?.watch?.ignored).to.eql('**/*')
expect(viteConfig.server?.hmr).to.be.false
})

it('uses defaults in open mode', async () => {
viteDevServerConfig.cypressConfig.isTextTerminal = false
const viteConfig = await createViteDevServerConfig(viteDevServerConfig, vite)

expect(viteConfig.server?.watch?.ignored).to.be.undefined
expect(viteConfig.server?.hmr).to.be.undefined
})
})
})
13 changes: 10 additions & 3 deletions npm/webpack-dev-server/src/createWebpackDevServer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import debugLib from 'debug'
import type { Configuration } from 'webpack-dev-server-3'
import type { Configuration as WebpackDevServer3Configuration } from 'webpack-dev-server-3'
import type { Configuration as WebpackDevServer4Configuration } from 'webpack-dev-server'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding types to these config objects 💯


import type { WebpackDevServerConfig } from './devServer'
import type { SourceRelativeWebpackResult } from './helpers/sourceRelativeWebpackModules'
Expand Down Expand Up @@ -66,8 +67,9 @@ function webpackDevServer4 (
finalWebpackConfig: Record<string, any>,
) {
const { devServerConfig: { cypressConfig: { devServerPublicPathRoute } } } = config
const isOpenMode = !config.devServerConfig.cypressConfig.isTextTerminal
const WebpackDevServer = config.sourceWebpackModulesResult.webpackDevServer.module
const webpackDevServerConfig = {
const webpackDevServerConfig: WebpackDevServer4Configuration = {
host: '127.0.0.1',
port: 'auto',
// @ts-ignore
Expand All @@ -77,6 +79,8 @@ function webpackDevServer4 (
stats: finalWebpackConfig.stats ?? 'minimal',
},
hot: false,
// Only enable file watching & reload when executing tests in `open` mode
liveReload: isOpenMode,
}

const server = new WebpackDevServer(webpackDevServerConfig, compiler)
Expand All @@ -93,8 +97,9 @@ function webpackDevServer3 (
finalWebpackConfig: Record<string, any>,
) {
const { devServerConfig: { cypressConfig: { devServerPublicPathRoute } } } = config
const isOpenMode = !config.devServerConfig.cypressConfig.isTextTerminal
const WebpackDevServer = config.sourceWebpackModulesResult.webpackDevServer.module
const webpackDevServerConfig: Configuration = {
const webpackDevServerConfig: WebpackDevServer3Configuration = {
// @ts-ignore
...finalWebpackConfig.devServer ?? {},
hot: false,
Expand All @@ -103,6 +108,8 @@ function webpackDevServer3 (
publicPath: devServerPublicPathRoute,
noInfo: false,
stats: finalWebpackConfig.stats ?? 'minimal',
// Only enable file watching & reload when executing tests in `open` mode
liveReload: isOpenMode,
}

const server = new WebpackDevServer(compiler, webpackDevServerConfig)
Expand Down
8 changes: 8 additions & 0 deletions npm/webpack-dev-server/src/makeDefaultWebpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function makeDefaultWebpackConfig (
importPath,
} = config.sourceWebpackModulesResult.htmlWebpackPlugin
const indexHtmlFile = config.devServerConfig.cypressConfig.indexHtmlFile
const isRunMode = config.devServerConfig.cypressConfig.isTextTerminal
const HtmlWebpackPlugin = _HtmlWebpackPlugin as typeof import('html-webpack-plugin-5')

debug(`Using HtmlWebpackPlugin version ${version} from ${importPath}`)
Expand Down Expand Up @@ -54,6 +55,13 @@ export function makeDefaultWebpackConfig (
devtool: 'inline-source-map',
} as any

if (isRunMode) {
// Disable file watching when executing tests in `run` mode
finalConfig.watchOptions = {
ignored: '**/*',
}
}

if (config.sourceWebpackModulesResult.webpackDevServer.majorVersion === 4) {
return {
...finalConfig,
Expand Down
122 changes: 76 additions & 46 deletions npm/webpack-dev-server/test/devServer-e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import path from 'path'
import { expect } from 'chai'
import { once, EventEmitter } from 'events'
import http from 'http'
import fs from 'fs'
import fs from 'fs-extra'

import { devServer } from '..'
import { restoreLoadHook } from '../src/helpers/sourceRelativeWebpackModules'
import './support'
import type { ConfigHandler } from '../src/devServer'

const requestSpecFile = (file: string, port: number) => {
return new Promise((res) => {
Expand Down Expand Up @@ -34,7 +35,7 @@ const requestSpecFile = (file: string, port: number) => {

const root = path.join(__dirname, '..')

const webpackConfig = {
const webpackConfig: ConfigHandler = {
devServer: { static: { directory: root } },
}

Expand All @@ -48,6 +49,20 @@ const createSpecs = (name: string): Cypress.Cypress['spec'][] => {
]
}

type DevServerCloseFn = Awaited<ReturnType<typeof devServer>>['close']

const closeServer = async (closeFn: DevServerCloseFn) => {
await new Promise<void>((resolve, reject) => {
closeFn((err?: Error) => {
if (err) {
return reject(err)
}

resolve()
})
})
}

const cypressConfig = {
projectRoot: root,
supportFile: '',
Expand Down Expand Up @@ -78,15 +93,7 @@ describe('#devServer', () => {

expect(response).to.eq('const foo = () => {}\n')

await new Promise<void>((resolve, reject) => {
close((err) => {
if (err) {
return reject(err)
}

resolve()
})
})
await closeServer(close)
})

it('serves specs in directory with [] chars via a webpack dev server', async () => {
Expand All @@ -101,9 +108,7 @@ describe('#devServer', () => {

expect(response).to.eq(`it('this is a spec with a path containing []', () => {})\n`)

return new Promise((res) => {
close(() => res())
})
return closeServer(close)
})

it('serves specs in directory with non English chars via a webpack dev server', async () => {
Expand All @@ -118,9 +123,7 @@ describe('#devServer', () => {

expect(response).to.eq(`it('サイプレス', () => {})\n`)

return new Promise((res) => {
close(() => res())
})
return closeServer(close)
})

it('serves specs in directory with ... in the file name via a webpack dev server', async () => {
Expand All @@ -135,9 +138,7 @@ describe('#devServer', () => {

expect(response).to.eq(`it('...bar', () => {})\n`)

return new Promise((res) => {
close(() => res())
})
return closeServer(close)
})

it('serves a file with spaces via a webpack dev server', async () => {
Expand All @@ -152,9 +153,7 @@ describe('#devServer', () => {

expect(response).to.eq(`it('this is a spec with a path containing a space', () => {})\n`)

return new Promise((res) => {
close(() => res())
})
return closeServer(close)
})

it('emits dev-server:compile:success event on successful compilation', async () => {
Expand All @@ -167,18 +166,13 @@ describe('#devServer', () => {
})

await once(devServerEvents, 'dev-server:compile:success')
await new Promise<void>((resolve, reject) => {
close((err) => {
if (err) {
return reject(err)
}

resolve()
})
})
await closeServer(close)
})

it('touches browser.js when a spec file is added and recompile', async function () {
// File watching only enabled when running in `open` mode
cypressConfig.isTextTerminal = false
const devServerEvents = new EventEmitter()
const { close } = await devServer({
webpackConfig,
Expand All @@ -203,14 +197,58 @@ describe('#devServer', () => {

expect(oldmtime).to.not.equal(updatedmtime)

await new Promise<void>((resolve, reject) => {
close((err) => {
if (err) {
return reject(err)
await closeServer(close)
})

;[{
title: 'does not watch/recompile files in `run` mode',
isRunMode: true,
updateExpected: false,
message: 'Files should not be watched in `run` mode',
}, {
title: 'watches and recompiles files on change in `open` mode',
isRunMode: false,
updateExpected: true,
message: 'Files should be watched and automatically rebuild on update in `open` mode',
}].forEach(({ title, isRunMode, updateExpected, message }) => {
it(title, async () => {
const originalContent = await fs.readFile(`./test/fixtures/dependency.js`)

try {
cypressConfig.devServerPublicPathRoute = '/__cypress/src'
cypressConfig.isTextTerminal = isRunMode
const devServerEvents = new EventEmitter()
const { close, port } = await devServer({
webpackConfig: {},
cypressConfig,
specs: createSpecs('bar.spec.js'),
devServerEvents,
})

// Wait for initial "ready" from server
await once(devServerEvents, 'dev-server:compile:success')

// Get the initial version of the bundled spec
const original = await requestSpecFile('/__cypress/src/spec-0.js', port)

// Update a dependency of the spec
await fs.writeFile('./test/fixtures/dependency.js', `window.TEST = true;${originalContent}`)
// Brief wait to give server time to detect changes
await new Promise((resolve) => setTimeout(resolve, 500))

// Re-fetch the spec
const updated = await requestSpecFile('/__cypress/src/spec-0.js', port)

if (updateExpected) {
expect(original, message).not.to.equal(updated)
} else {
expect(original, message).to.equal(updated)
}

resolve()
})
await closeServer(close)
} finally {
fs.writeFile('./test/fixtures/dependency.js', originalContent)
}
})
})

Expand All @@ -229,15 +267,7 @@ describe('#devServer', () => {

expect(response).to.eq('const foo = () => {}\n')

await new Promise<void>((resolve, reject) => {
close((err) => {
if (err) {
return reject(err)
}

resolve()
})
})
await closeServer(close)
})
})
.timeout(5000)
2 changes: 2 additions & 0 deletions npm/webpack-dev-server/test/fixtures/bar.spec.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
import './dependency'

const bar = () => {}
1 change: 1 addition & 0 deletions npm/webpack-dev-server/test/fixtures/dependency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {}
Loading