Skip to content
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

feat: allow to use TypeScript in the config file #18300

Merged
merged 7 commits into from
Oct 2, 2021
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
9 changes: 9 additions & 0 deletions packages/server/__snapshots__/3_config_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,13 @@ We found an invalid value in the file: \`cypress.json\`

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}\`

`

exports['e2e config throws error when multiple default config file are found in project 1'] = `
There is both a \`cypress.config.js\` and a \`cypress.config.ts\` at the location below:
/foo/bar/.projects/pristine

Cypress does not know which one to read for config. Please remove one of the two and try again.


`
3 changes: 1 addition & 2 deletions packages/server/lib/configFiles.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
// the first file is the default created file
export const CYPRESS_CONFIG_FILES = ['cypress.json', 'cypress.config.js']
export const CYPRESS_CONFIG_FILES = ['cypress.json', 'cypress.config.js', 'cypress.config.ts']
2 changes: 1 addition & 1 deletion packages/server/lib/plugins/child/run_plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ const Promise = require('bluebird')
const preprocessor = require('./preprocessor')
const devServer = require('./dev-server')
const resolve = require('../../util/resolve')
const tsNodeUtil = require('../../util/ts_node')
const browserLaunch = require('./browser_launch')
const task = require('./task')
const util = require('../util')
const validateEvent = require('./validate_event')
const tsNodeUtil = require('./ts_node')

let registeredEventsById = {}
let registeredEventsByName = {}
Expand Down
11 changes: 11 additions & 0 deletions packages/server/lib/util/require_async_child.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
require('graceful-fs').gracefulify(require('fs'))
const stripAnsi = require('strip-ansi')
const debug = require('debug')('cypress:server:require_async:child')
const tsNodeUtil = require('./ts_node')
const util = require('../plugins/util')
const ipc = util.wrapIpc(process)

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

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

let tsRegistered = false

run(ipc, file, projectRoot)

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

if (!tsRegistered && requiredFile.endsWith('.ts')) {
debug('register typescript for required file')
tsNodeUtil.register(projectRoot, requiredFile)

// ensure typescript is only registered once
tsRegistered = true
}

process.on('uncaughtException', (err) => {
debug('uncaught exception:', util.serializeError(err))
ipc.send('error', util.serializeError(err))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
const debug = require('debug')('cypress:server:ts-node')
const path = require('path')
const tsnode = require('ts-node')
const resolve = require('../../util/resolve')
const resolve = require('./resolve')

const getTsNodeOptions = (tsPath, pluginsFile) => {
const getTsNodeOptions = (tsPath, registeredFile) => {
return {
compiler: tsPath, // use the user's installed typescript
compilerOptions: {
module: 'CommonJS',
},
// resolves tsconfig.json starting from the plugins directory
// instead of the cwd (the project root)
dir: path.dirname(pluginsFile),
dir: path.dirname(registeredFile),
transpileOnly: true, // transpile only (no type-check) for speed
}
}

const register = (projectRoot, pluginsFile) => {
const register = (projectRoot, registeredFile) => {
try {
debug('projectRoot path: %s', projectRoot)
debug('registeredFile: %s', registeredFile)
const tsPath = resolve.typescript(projectRoot)

if (!tsPath) return

const tsOptions = getTsNodeOptions(tsPath, pluginsFile)

debug('typescript path: %s', tsPath)

const tsOptions = getTsNodeOptions(tsPath, registeredFile)

debug('registering project TS with options %o', tsOptions)

require('tsconfig-paths/register')
Expand Down
30 changes: 30 additions & 0 deletions packages/server/test/e2e/3_config_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const e2e = require('../support/helpers/e2e').default
const { fs } = require('../../lib/util/fs')
const path = require('path')
const Fixtures = require('../support/helpers/fixtures')

describe('e2e config', () => {
Expand Down Expand Up @@ -55,9 +57,37 @@ describe('e2e config', () => {
})
})

it('supports custom configFile in TypeScript', function () {
return e2e.exec(this, {
project: Fixtures.projectPath('config-with-custom-file-ts'),
configFile: 'cypress.config.custom.ts',
})
})

it('supports custom configFile in a default JavaScript file', function () {
return e2e.exec(this, {
project: Fixtures.projectPath('config-with-js'),
})
})

it('supports custom configFile in a default TypeScript file', function () {
return e2e.exec(this, {
project: Fixtures.projectPath('config-with-ts'),
})
})

it('throws error when multiple default config file are found in project', function () {
const projectRoot = Fixtures.projectPath('pristine')

return Promise.all([
fs.writeFile(path.join(projectRoot, 'cypress.config.js'), 'module.exports = {}'),
fs.writeFile(path.join(projectRoot, 'cypress.config.ts'), 'export default {}'),
]).then(() => {
return e2e.exec(this, {
project: projectRoot,
expectedExitCode: 1,
snapshot: true,
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const config: Record<string, any> = {
pageLoadTimeout: 10000,
e2e: {
defaultCommandTimeout: 500,
videoCompression: 20,
},
}

export default config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
it('overrides config', () => {
// overrides come from plugins
expect(Cypress.config('defaultCommandTimeout')).to.eq(500)
expect(Cypress.config('videoCompression')).to.eq(20)

// overrides come from CLI
expect(Cypress.config('pageLoadTimeout')).to.eq(10000)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
pageLoadTimeout: 10000,
e2e: {
defaultCommandTimeout: 500,
videoCompression: 20,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
it('overrides config', () => {
// overrides come from plugins
expect(Cypress.config('defaultCommandTimeout')).to.eq(500)
expect(Cypress.config('videoCompression')).to.eq(20)

// overrides come from CLI
expect(Cypress.config('pageLoadTimeout')).to.eq(10000)
})
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const util = require(`${root}../../lib/plugins/util`)
const resolve = require(`${root}../../lib/util/resolve`)
const browserUtils = require(`${root}../../lib/browsers/utils`)
const Fixtures = require(`${root}../../test/support/helpers/fixtures`)
const tsNodeUtil = require(`${root}../../lib/plugins/child/ts_node`)
const tsNodeUtil = require(`${root}../../lib/util/ts_node`)

const runPlugins = require(`${root}../../lib/plugins/child/run_plugins`)

Expand Down
4 changes: 2 additions & 2 deletions packages/server/test/unit/plugins/child/ts_node_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const tsnode = require('ts-node')

const resolve = require(`${root}../../lib/util/resolve`)

const tsNodeUtil = require(`${root}../../lib/plugins/child/ts_node`)
const tsNodeUtil = require(`${root}../../lib/util/ts_node`)

describe('lib/plugins/child/ts_node', () => {
describe('lib/util/ts_node', () => {
beforeEach(() => {
sinon.stub(tsnode, 'register')
sinon.stub(resolve, 'typescript').returns('/path/to/typescript.js')
Expand Down