Skip to content

Commit 5b8f6da

Browse files
Barthélémy Ledouxflotwig
authored andcommitted
feat: allow to use TypeScript in the config file (#18300)
* feat: allow to use typescritpt in the config file * add end to end test * test: add test for the default config file ts * tests: add a tests for language conflict * fix: only compile typescript if main file is ts Co-authored-by: Zach Bloomquist <github@chary.us> Co-authored-by: Zach Bloomquist <github@chary.us>
1 parent b8a4a07 commit 5b8f6da

File tree

12 files changed

+96
-12
lines changed

12 files changed

+96
-12
lines changed

packages/server/__snapshots__/3_config_spec.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,13 @@ We found an invalid value in the file: \`cypress.json\`
151151
152152
Found an error while validating the \`browsers\` list. Expected \`family\` to be either chromium or firefox. Instead the value was: \`{"name":"bad browser","family":"unknown family","displayName":"Bad browser","version":"no version","path":"/path/to","majorVersion":123}\`
153153
154+
`
155+
156+
exports['e2e config throws error when multiple default config file are found in project 1'] = `
157+
There is both a \`cypress.config.js\` and a \`cypress.config.ts\` at the location below:
158+
/foo/bar/.projects/pristine
159+
160+
Cypress does not know which one to read for config. Please remove one of the two and try again.
161+
162+
154163
`

packages/server/lib/configFiles.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
// the first file is the default created file
2-
export const CYPRESS_CONFIG_FILES = ['cypress.json', 'cypress.config.js']
1+
export const CYPRESS_CONFIG_FILES = ['cypress.json', 'cypress.config.js', 'cypress.config.ts']

packages/server/lib/plugins/child/run_plugins.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ const Promise = require('bluebird')
77
const preprocessor = require('./preprocessor')
88
const devServer = require('./dev-server')
99
const resolve = require('../../util/resolve')
10+
const tsNodeUtil = require('../../util/ts_node')
1011
const browserLaunch = require('./browser_launch')
1112
const task = require('./task')
1213
const util = require('../util')
1314
const validateEvent = require('./validate_event')
14-
const tsNodeUtil = require('./ts_node')
1515

1616
let registeredEventsById = {}
1717
let registeredEventsByName = {}

packages/server/lib/util/require_async_child.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
require('graceful-fs').gracefulify(require('fs'))
22
const stripAnsi = require('strip-ansi')
33
const debug = require('debug')('cypress:server:require_async:child')
4+
const tsNodeUtil = require('./ts_node')
45
const util = require('../plugins/util')
56
const ipc = util.wrapIpc(process)
67

78
require('./suppress_warnings').suppress()
89

910
const { file, projectRoot } = require('minimist')(process.argv.slice(2))
1011

12+
let tsRegistered = false
13+
1114
run(ipc, file, projectRoot)
1215

1316
/**
@@ -24,6 +27,14 @@ function run (ipc, requiredFile, projectRoot) {
2427
throw new Error('Unexpected: projectRoot should be a string')
2528
}
2629

30+
if (!tsRegistered && requiredFile.endsWith('.ts')) {
31+
debug('register typescript for required file')
32+
tsNodeUtil.register(projectRoot, requiredFile)
33+
34+
// ensure typescript is only registered once
35+
tsRegistered = true
36+
}
37+
2738
process.on('uncaughtException', (err) => {
2839
debug('uncaught exception:', util.serializeError(err))
2940
ipc.send('error', util.serializeError(err))

packages/server/lib/plugins/child/ts_node.js renamed to packages/server/lib/util/ts_node.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
11
const debug = require('debug')('cypress:server:ts-node')
22
const path = require('path')
33
const tsnode = require('ts-node')
4-
const resolve = require('../../util/resolve')
4+
const resolve = require('./resolve')
55

6-
const getTsNodeOptions = (tsPath, pluginsFile) => {
6+
const getTsNodeOptions = (tsPath, registeredFile) => {
77
return {
88
compiler: tsPath, // use the user's installed typescript
99
compilerOptions: {
1010
module: 'CommonJS',
1111
},
1212
// resolves tsconfig.json starting from the plugins directory
1313
// instead of the cwd (the project root)
14-
dir: path.dirname(pluginsFile),
14+
dir: path.dirname(registeredFile),
1515
transpileOnly: true, // transpile only (no type-check) for speed
1616
}
1717
}
1818

19-
const register = (projectRoot, pluginsFile) => {
19+
const register = (projectRoot, registeredFile) => {
2020
try {
21+
debug('projectRoot path: %s', projectRoot)
22+
debug('registeredFile: %s', registeredFile)
2123
const tsPath = resolve.typescript(projectRoot)
2224

2325
if (!tsPath) return
2426

25-
const tsOptions = getTsNodeOptions(tsPath, pluginsFile)
26-
2727
debug('typescript path: %s', tsPath)
28+
29+
const tsOptions = getTsNodeOptions(tsPath, registeredFile)
30+
2831
debug('registering project TS with options %o', tsOptions)
2932

3033
require('tsconfig-paths/register')

packages/server/test/e2e/3_config_spec.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const e2e = require('../support/helpers/e2e').default
2+
const { fs } = require('../../lib/util/fs')
3+
const path = require('path')
24
const Fixtures = require('../support/helpers/fixtures')
35

46
describe('e2e config', () => {
@@ -55,9 +57,37 @@ describe('e2e config', () => {
5557
})
5658
})
5759

60+
it('supports custom configFile in TypeScript', function () {
61+
return e2e.exec(this, {
62+
project: Fixtures.projectPath('config-with-custom-file-ts'),
63+
configFile: 'cypress.config.custom.ts',
64+
})
65+
})
66+
5867
it('supports custom configFile in a default JavaScript file', function () {
5968
return e2e.exec(this, {
6069
project: Fixtures.projectPath('config-with-js'),
6170
})
6271
})
72+
73+
it('supports custom configFile in a default TypeScript file', function () {
74+
return e2e.exec(this, {
75+
project: Fixtures.projectPath('config-with-ts'),
76+
})
77+
})
78+
79+
it('throws error when multiple default config file are found in project', function () {
80+
const projectRoot = Fixtures.projectPath('pristine')
81+
82+
return Promise.all([
83+
fs.writeFile(path.join(projectRoot, 'cypress.config.js'), 'module.exports = {}'),
84+
fs.writeFile(path.join(projectRoot, 'cypress.config.ts'), 'export default {}'),
85+
]).then(() => {
86+
return e2e.exec(this, {
87+
project: projectRoot,
88+
expectedExitCode: 1,
89+
snapshot: true,
90+
})
91+
})
92+
})
6393
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const config: Record<string, any> = {
2+
pageLoadTimeout: 10000,
3+
e2e: {
4+
defaultCommandTimeout: 500,
5+
videoCompression: 20,
6+
},
7+
}
8+
9+
export default config
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
it('overrides config', () => {
2+
// overrides come from plugins
3+
expect(Cypress.config('defaultCommandTimeout')).to.eq(500)
4+
expect(Cypress.config('videoCompression')).to.eq(20)
5+
6+
// overrides come from CLI
7+
expect(Cypress.config('pageLoadTimeout')).to.eq(10000)
8+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default {
2+
pageLoadTimeout: 10000,
3+
e2e: {
4+
defaultCommandTimeout: 500,
5+
videoCompression: 20,
6+
},
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
it('overrides config', () => {
2+
// overrides come from plugins
3+
expect(Cypress.config('defaultCommandTimeout')).to.eq(500)
4+
expect(Cypress.config('videoCompression')).to.eq(20)
5+
6+
// overrides come from CLI
7+
expect(Cypress.config('pageLoadTimeout')).to.eq(10000)
8+
})

0 commit comments

Comments
 (0)