Skip to content

Commit f9ce67c

Browse files
fix: detect newly added specs in dev-server compilation (#17950)
Co-authored-by: Lachlan Miller <[email protected]>
1 parent f1fb88a commit f9ce67c

File tree

9 files changed

+114
-64
lines changed

9 files changed

+114
-64
lines changed

npm/webpack-dev-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"build": "tsc",
88
"build-prod": "tsc",
9-
"test": "node ./test-wds-3.js",
9+
"test": "node ./test-deps.js",
1010
"test-all": "tsc && mocha -r @packages/ts/register test/**/*.spec.ts test/*.spec.ts --exit",
1111
"watch": "tsc -w"
1212
},

npm/webpack-dev-server/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export async function startDevServer (startDevServerArgs: StartDevServer, exitPr
2020

2121
return new Promise<ResolvedDevServerConfig>(async (resolve, reject) => {
2222
if (webpackDevServerFacts.isV3()) {
23+
// @ts-ignore
2324
const server: Server = webpackDevServer.listen(0, '127.0.0.1', () => {
2425
// FIXME: handle address returning a string
2526
const port = (server.address() as AddressInfo).port

npm/webpack-dev-server/src/loader.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import debugFn from 'debug'
55
import * as path from 'path'
66
import { CypressCTWebpackContext } from './plugin'
77
const debug = debugFn('cypress:webpack-dev-server:webpack')
8+
import type { LoaderContext } from 'webpack'
89

910
/**
1011
* @param {ComponentSpec} file spec to create import string from.
@@ -49,7 +50,11 @@ function buildSpecs (projectRoot: string, files: Cypress.Cypress['spec'][] = [])
4950
}
5051

5152
// Runs the tests inside the iframe
52-
export default function loader (this: CypressCTWebpackContext) {
53+
export default function loader (this: CypressCTWebpackContext & LoaderContext<void>) {
54+
// In Webpack 5, a spec added after the dev-server is created won't
55+
// be included in the compilation. Disabling the caching of this loader ensures
56+
// we regenerate our specs and include any new ones in the compilation.
57+
this.cacheable(false)
5358
const { files, projectRoot, supportFile } = this._cypress
5459

5560
const supportFileAbsolutePath = supportFile ? JSON.stringify(path.resolve(projectRoot, supportFile)) : undefined

npm/webpack-dev-server/src/startServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export async function start ({ webpackConfig: userWebpackConfig, template, optio
8383
hot: false,
8484
}
8585

86-
// @ts-expect-error Webpack types are clashing between Webpack and WebpackDevServer
86+
// @ts-ignore Webpack types are clashing between Webpack and WebpackDevServer
8787
return new WebpackDevServer(webpackDevServerConfig, compiler)
8888
}
8989

npm/webpack-dev-server/test-deps.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const execa = require('execa')
2+
const pkg = require('./package.json')
3+
const fs = require('fs')
4+
5+
/**
6+
* This file installs dependencies that we support but don't have coverage for.
7+
* We read package.json, update the dependency, then re-run yarn install.
8+
* After it finishes, pass or fail,
9+
* we revert the package.json back to the original state.
10+
*/
11+
const main = async () => {
12+
const depsToTest = [
13+
[
14+
{
15+
name: 'webpack-dev-server',
16+
version: '3.11.0',
17+
type: 'devDependencies',
18+
},
19+
],
20+
[
21+
{ name: 'webpack', version: '5.53.0', type: 'devDependencies' },
22+
{
23+
name: 'html-webpack-plugin',
24+
version: '5.3.2',
25+
type: 'devDependencies',
26+
},
27+
],
28+
]
29+
const originalPkg = JSON.stringify(pkg, null, 2)
30+
31+
const install = () => execa('yarn', ['install', '--ignore-scripts'], { stdio: 'inherit' })
32+
33+
const exit = async (exitCode) => {
34+
fs.writeFileSync('package.json', originalPkg, 'utf8')
35+
await install()
36+
process.exit(exitCode)
37+
}
38+
39+
for (const deps of depsToTest) {
40+
const pkg = JSON.parse(originalPkg)
41+
const depsInfo = JSON.stringify(deps)
42+
43+
deps.forEach(({ type, name, version }) => (pkg[type][name] = version))
44+
45+
// eslint-disable-next-line no-console
46+
console.log('[@cypress/webpack-dev-server]: updating package.json...')
47+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2), 'utf8')
48+
49+
// eslint-disable-next-line no-console
50+
console.log('[@cypress/webpack-dev-server]: install dependencies...')
51+
await install()
52+
53+
// eslint-disable-next-line no-console
54+
console.log(
55+
`[@cypress/webpack-dev-server]: Testing with deps: ${depsInfo}`,
56+
)
57+
58+
const { exitCode } = await execa('yarn', ['test-all'], {
59+
stdio: 'inherit',
60+
})
61+
62+
if (typeof exitCode !== 'number') {
63+
// eslint-disable-next-line no-console
64+
console.error(
65+
`Testing with deps: ${depsInfo} finished with missing exit code from execa (received ${exitCode})`,
66+
)
67+
}
68+
69+
if (exitCode !== 0) {
70+
exit(exitCode)
71+
}
72+
}
73+
exit(0)
74+
}
75+
76+
// execute main function if called from command line
77+
if (require.main === module) {
78+
main()
79+
}

npm/webpack-dev-server/test-wds-3.js

Lines changed: 0 additions & 49 deletions
This file was deleted.

npm/webpack-dev-server/test/e2e.spec.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ describe('#startDevServer', () => {
118118

119119
return new Promise((res) => {
120120
devServerEvents.on('dev-server:compile:error', (err: string) => {
121-
expect(err).to.contain('./test/fixtures/compilation-fails.spec.js 1:5')
121+
if (webpackDevServerFacts.isV3()) {
122+
expect(err).to.contain('./test/fixtures/compilation-fails.spec.js 1:5')
123+
}
124+
122125
expect(err).to.contain('Module parse failed: Unexpected token (1:5)')
123126
expect(err).to.contain('You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders')
124127
expect(err).to.contain('> this is an invalid spec file')
@@ -128,7 +131,7 @@ describe('#startDevServer', () => {
128131
})
129132
})
130133

131-
it('touches browser.js when a spec file is added', async function () {
134+
it('touches browser.js when a spec file is added and recompile', async function () {
132135
const devServerEvents = new EventEmitter()
133136
const { close } = await startDevServer({
134137
webpackConfig,
@@ -140,19 +143,27 @@ describe('#startDevServer', () => {
140143
})
141144

142145
const newSpec: Cypress.Cypress['spec'] = {
143-
name: './some-newly-created-spec.js',
144-
relative: './some-newly-created-spec.js',
145-
absolute: '/some-newly-created-spec.js',
146+
name: `${root}/test/fixtures/bar.spec.js`,
147+
relative: `${root}/test/fixtures/bar.spec.js`,
148+
absolute: `${root}/test/fixtures/bar.spec.js`,
146149
}
147150

148151
const oldmtime = fs.statSync('./dist/browser.js').mtimeMs
149152

150-
return new Promise((res) => {
151-
devServerEvents.emit('dev-server:specs:changed', [newSpec])
152-
const updatedmtime = fs.statSync('./dist/browser.js').mtimeMs
153+
let firstCompile = true
153154

154-
expect(oldmtime).to.not.equal(updatedmtime)
155-
close(() => res())
155+
return new Promise((res) => {
156+
devServerEvents.on('dev-server:compile:success', () => {
157+
if (firstCompile) {
158+
firstCompile = false
159+
devServerEvents.emit('dev-server:specs:changed', [newSpec])
160+
const updatedmtime = fs.statSync('./dist/browser.js').mtimeMs
161+
162+
expect(oldmtime).to.not.equal(updatedmtime)
163+
} else {
164+
close(() => res())
165+
}
166+
})
156167
})
157168
})
158169

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const bar = () => {}

npm/webpack-preprocessor/test-webpack-5.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ const fs = require('fs')
1414
const main = async () => {
1515
const originalPkg = JSON.stringify(pkg, null, 2)
1616

17+
const install = () => execa('yarn', ['install', '--ignore-scripts'], { stdio: 'inherit' })
18+
1719
const resetPkg = async () => {
1820
fs.writeFileSync('package.json', originalPkg, 'utf8')
19-
await execa('yarn', ['install'], { stdio: 'inherit' })
21+
await install()
2022
}
2123

2224
const checkExit = async ({ exitCode, step }) => {
@@ -40,7 +42,7 @@ const main = async () => {
4042

4143
// eslint-disable-next-line no-console
4244
console.log('[@cypress/webpack-preprocessor]: install dependencies...')
43-
await execa('yarn', ['install'], { stdio: 'inherit' })
45+
await install()
4446

4547
const unit = await execa('yarn', ['test-unit'], { stdio: 'inherit' })
4648

0 commit comments

Comments
 (0)