Skip to content
This repository was archived by the owner on Oct 1, 2020. It is now read-only.

Commit 85ef427

Browse files
fix: The 'rerun' event is no longer emitted on the initial bundling of a file (#103)
1 parent ba0dc3d commit 85ef427

File tree

6 files changed

+200
-41
lines changed

6 files changed

+200
-41
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ examples/use-babelrc/cypress/integration/spec.js
1313
dist
1414
tsconfig.json
1515
.vscode/
16+
.history

index.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ const debug = require('debug')('cypress:webpack')
1111
const debugStats = require('debug')('cypress:webpack:stats')
1212

1313
type FilePath = string
14+
interface BundleObject {
15+
promise: Promise<FilePath>
16+
initial: boolean
17+
}
1418

1519
// bundle promises from input spec filename to output bundled file paths
16-
let bundles: {[key: string]: Promise<FilePath>} = {}
20+
let bundles: {[key: string]: BundleObject} = {}
1721

1822
// we don't automatically load the rules, so that the babel dependencies are
1923
// not required if a user passes in their own configuration
@@ -164,7 +168,7 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
164168
if (bundles[filePath]) {
165169
debug(`already have bundle for ${filePath}`)
166170

167-
return bundles[filePath]
171+
return bundles[filePath].promise
168172
}
169173

170174
const defaultWebpackOptions = getDefaultWebpackOptions()
@@ -229,7 +233,10 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
229233

230234
// cache the bundle promise, so it can be returned if this function
231235
// is invoked again with the same filePath
232-
bundles[filePath] = latestBundle.promise
236+
bundles[filePath] = {
237+
promise: latestBundle.promise,
238+
initial: true,
239+
}
233240

234241
const rejectWithErr = (err: Error) => {
235242
err = quietErrorMessage(err)
@@ -294,19 +301,24 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
294301
// we overwrite the latest bundle, so that a new call to this function
295302
// returns a promise that resolves when the bundling is finished
296303
latestBundle = createDeferred<string>()
297-
bundles[filePath] = latestBundle.promise
304+
bundles[filePath].promise = latestBundle.promise
298305

299-
bundles[filePath].finally(() => {
300-
debug('- compile finished for', filePath)
306+
bundles[filePath].promise.finally(() => {
307+
debug('- compile finished for %s, initial? %s', filePath, bundles[filePath].initial)
301308
// when the bundling is finished, emit 'rerun' to let Cypress
302-
// know to rerun the spec
303-
file.emit('rerun')
309+
// know to rerun the spec, but NOT when it is the initial
310+
// bundling of the file
311+
if (!bundles[filePath].initial) {
312+
file.emit('rerun')
313+
}
314+
315+
bundles[filePath].initial = false
304316
})
305317
// we suppress unhandled rejections so they don't bubble up to the
306318
// unhandledRejection handler and crash the process. Cypress will
307319
// eventually take care of the rejection when the file is requested.
308320
// note that this does not work if attached to latestBundle.promise
309-
// for some reason. it only works when attached after .tap ¯\_(ツ)_/¯
321+
// for some reason. it only works when attached after .finally ¯\_(ツ)_/¯
310322
.suppressUnhandledRejections()
311323
}
312324

@@ -341,7 +353,7 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
341353

342354
// return the promise, which will resolve with the outputPath or reject
343355
// with any error encountered
344-
return bundles[filePath]
356+
return bundles[filePath].promise
345357
}
346358
}
347359

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
"secure": "nsp check",
1515
"semantic-release": "semantic-release",
1616
"size": "npm pack --dry",
17-
"pretest": "yarn lint && yarn build",
1817
"test": "yarn test-unit && yarn test-e2e",
1918
"test-debug": "node --inspect --debug-brk ./node_modules/.bin/_mocha",
2019
"test-e2e": "mocha test/e2e/*.spec.*",
2120
"test-unit": "mocha test/unit/*.spec.*",
2221
"test-watch": "yarn test-unit & chokidar '**/*.(js|ts)' 'test/unit/*.(js|ts)' -c 'yarn test-unit'",
23-
"types": "tsc --noEmit"
22+
"types": "tsc --noEmit",
23+
"watch": "yarn build --watch"
2424
},
2525
"husky": {
2626
"hooks": {
@@ -37,6 +37,7 @@
3737
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3",
3838
"@babel/preset-env": "^7.0.0",
3939
"@cypress/eslint-plugin-dev": "5.0.0",
40+
"@fellow/eslint-plugin-coffee": "0.4.13",
4041
"@types/webpack": "4.41.12",
4142
"@typescript-eslint/eslint-plugin": "2.31.0",
4243
"@typescript-eslint/parser": "2.31.0",

test/e2e/compilation.spec.js

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,20 @@ const normalizeErrMessage = (message) => {
1919
const fixturesDir = path.join(__dirname, '..', 'fixtures')
2020
const outputDir = path.join(__dirname, '..', '_test-output')
2121

22+
const createFile = ({ name = 'example_spec.js', shouldWatch = false } = {}) => {
23+
return Object.assign(new EventEmitter(), {
24+
filePath: path.join(outputDir, name),
25+
outputPath: path.join(outputDir, name.replace('.', '_output.')),
26+
shouldWatch,
27+
})
28+
}
29+
2230
describe('webpack preprocessor - e2e', () => {
23-
let run
2431
let file
2532

2633
beforeEach(async () => {
2734
preprocessor.__reset()
2835

29-
run = ({ options, keepFile, shouldWatch = false, fileName = 'example_spec.js' } = {}) => {
30-
if (!keepFile) {
31-
file = Object.assign(new EventEmitter(), {
32-
filePath: path.join(outputDir, fileName),
33-
outputPath: path.join(outputDir, fileName.replace('.', '_output.')),
34-
shouldWatch,
35-
})
36-
}
37-
38-
return preprocessor(options)(file)
39-
}
40-
4136
await fs.remove(outputDir)
4237
await fs.copy(fixturesDir, outputDir)
4338
})
@@ -54,14 +49,17 @@ describe('webpack preprocessor - e2e', () => {
5449
const options = preprocessor.defaultOptions
5550

5651
options.webpackOptions.mode = 'production' // snapshot will be minified
52+
file = createFile()
5753

58-
return run({ options }).then((outputPath) => {
54+
return preprocessor(options)(file).then((outputPath) => {
5955
snapshot(fs.readFileSync(outputPath).toString())
6056
})
6157
})
6258

6359
it('has less verbose "Module not found" error', () => {
64-
return run({ fileName: 'imports_nonexistent_file_spec.js' })
60+
file = createFile({ name: 'imports_nonexistent_file_spec.js' })
61+
62+
return preprocessor()(file)
6563
.then(() => {
6664
throw new Error('Should not resolve')
6765
})
@@ -71,7 +69,9 @@ describe('webpack preprocessor - e2e', () => {
7169
})
7270

7371
it('has less verbose syntax error', () => {
74-
return run({ fileName: 'syntax_error_spec.js' })
72+
file = createFile({ name: 'syntax_error_spec.js' })
73+
74+
return preprocessor()(file)
7575
.then(() => {
7676
throw new Error('Should not resolve')
7777
})
@@ -87,12 +87,14 @@ describe('webpack preprocessor - e2e', () => {
8787
throw new Error('Should not have trigger unhandled rejection')
8888
})
8989

90-
await run({ shouldWatch: true })
90+
file = createFile({ shouldWatch: true })
91+
92+
await preprocessor()(file)
9193
await fs.outputFile(file.filePath, '{')
9294

9395
await new Promise((resolve) => {
9496
setTimeout(() => {
95-
run({ keepFile: true, shouldWatch: true })
97+
preprocessor()(file)
9698
.catch((err) => {
9799
expect(err.stack).to.include('Unexpected token')
98100
resolve()
@@ -102,14 +104,29 @@ describe('webpack preprocessor - e2e', () => {
102104
})
103105

104106
it('triggers rerun on syntax error', async () => {
105-
await run({ shouldWatch: true })
107+
file = createFile({ shouldWatch: true })
108+
109+
await preprocessor()(file)
106110

107111
const _emit = sinon.spy(file, 'emit')
108112

109113
await fs.outputFile(file.filePath, '{')
110114

111115
await retry(() => expect(_emit).calledWith('rerun'))
112116
})
117+
118+
it('does not call rerun on initial build, but on subsequent builds', async () => {
119+
file = createFile({ shouldWatch: true })
120+
const _emit = sinon.spy(file, 'emit')
121+
122+
await preprocessor()(file)
123+
124+
expect(_emit).not.to.be.calledWith('rerun')
125+
126+
await fs.outputFile(file.filePath, 'console.log()')
127+
128+
await retry(() => expect(_emit).calledWith('rerun'))
129+
})
113130
})
114131

115132
function retry (fn, timeout = 1000) {

test/unit/index.spec.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const chai = require('chai')
44
const mockery = require('mockery')
5+
const Promise = require('bluebird')
56
const sinon = require('sinon')
67

78
const expect = chai.expect
@@ -247,12 +248,21 @@ describe('webpack preprocessor', function () {
247248
})
248249
})
249250

250-
it('emits "rerun" when shouldWatch is true and there is an update', function () {
251+
it('emits "rerun" when shouldWatch is true after there is an update', function () {
251252
this.file.shouldWatch = true
252253
this.compilerApi.watch.yields(null, this.statsApi)
253254
this.compilerApi.plugin.withArgs('compile').yields()
254255

255-
return this.run().then(() => {
256+
return this.run()
257+
.then(() => {
258+
expect(this.file.emit).not.to.be.calledWith('rerun')
259+
260+
this.compilerApi.plugin.withArgs('compile').yield()
261+
this.compilerApi.watch.yield(null, this.statsApi)
262+
263+
return Promise.delay(10) // give assertion time till next tick
264+
})
265+
.then(() => {
256266
expect(this.file.emit).to.be.calledWith('rerun')
257267
})
258268
})

0 commit comments

Comments
 (0)