Skip to content

Commit

Permalink
Merge branch 'develop' into issue-8361-fix-mjs-support
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbreiding authored Aug 25, 2020
2 parents be20c16 + 2a2cad6 commit ec76ab2
Show file tree
Hide file tree
Showing 20 changed files with 124 additions and 98 deletions.
11 changes: 7 additions & 4 deletions packages/driver/src/cypress/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -1010,12 +1010,15 @@ const create = (specWindow, mocha, Cypress, cy) => {
}

const maybeHandleRetry = (runnable, err) => {
if (!err) return

const r = runnable
const isHook = r.type === 'hook'
const isTest = r.type === 'test'
const test = getTest() || getTestFromHook(runnable, getTestById)
const isBeforeEachHook = isHook && !!r.hookName.match(/before each/)
const isAfterEachHook = isHook && !!r.hookName.match(/after each/)
const hookName = isHook && getHookName(r)
const isBeforeEachHook = isHook && !!hookName.match(/before each/)
const isAfterEachHook = isHook && !!hookName.match(/after each/)
const retryAbleRunnable = isTest || isBeforeEachHook || isAfterEachHook
const willRetry = (test._currentRetry < test._retries) && retryAbleRunnable

Expand Down Expand Up @@ -1196,8 +1199,8 @@ const create = (specWindow, mocha, Cypress, cy) => {

const isHook = runnable.type === 'hook'

const isAfterEachHook = isHook && runnable.hookName.match(/after each/)
const isBeforeEachHook = isHook && runnable.hookName.match(/before each/)
const isAfterEachHook = isHook && hookName.match(/after each/)
const isBeforeEachHook = isHook && hookName.match(/before each/)

// if we've been told to skip hooks at a certain nested level
// this happens if we're handling a runnable that is going to retry due to failing in a hook
Expand Down
20 changes: 20 additions & 0 deletions packages/runner/cypress/integration/issues/issue-8350.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const { createCypress } = require('../../support/helpers')
const { runIsolatedCypress } = createCypress()

// https://github.com/cypress-io/cypress/issues/8350
describe('issue-8350', () => {
it('does not hang on nested hook', () => {
runIsolatedCypress(() => {
before(() => {
beforeEach(() => {
})
})

describe('s1', () => {
it('t1', () => {
//
})
})
})
})
})
5 changes: 0 additions & 5 deletions packages/runner/cypress/support/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,6 @@ function createCypress (defaultOptions = {}) {
if (testsInOwnFile) return

generateMochaTestsForWin(specWindow, mochaTestsOrFile)
specWindow.before = () => {}
specWindow.beforeEach = () => {}
specWindow.afterEach = () => {}
specWindow.after = () => {}
specWindow.describe = () => {}
})

cy.stub(autCypress, 'run').snapshot(enableStubSnapshots).log(false).callsFake(runIsolatedCypress)
Expand Down
11 changes: 9 additions & 2 deletions packages/server/lib/plugins/child/run_plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ const execute = (ipc, event, ids, args = []) => {

let tsRegistered = false

module.exports = (ipc, pluginsFile, projectRoot) => {
const runPlugins = (ipc, pluginsFile, projectRoot) => {
debug('pluginsFile:', pluginsFile)
debug('project root:', projectRoot)
if (!projectRoot) {
Expand All @@ -181,7 +181,7 @@ module.exports = (ipc, pluginsFile, projectRoot) => {
})

if (!tsRegistered) {
registerTsNode(projectRoot)
registerTsNode(projectRoot, pluginsFile)

// ensure typescript is only registered once
tsRegistered = true
Expand Down Expand Up @@ -219,3 +219,10 @@ module.exports = (ipc, pluginsFile, projectRoot) => {
execute(ipc, event, ids, args)
})
}

// for testing purposes
runPlugins.__reset = () => {
tsRegistered = false
}

module.exports = runPlugins
5 changes: 2 additions & 3 deletions packages/server/lib/plugins/preprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const debug = require('debug')('cypress:server:preprocessor')
const Promise = require('bluebird')
const appData = require('../util/app_data')
const plugins = require('../plugins')
const resolve = require('./resolve')
const resolve = require('../util/resolve')

const errorMessage = function (err = {}) {
return (err.stack || err.annotated || err.message || err.toString())
Expand Down Expand Up @@ -46,8 +46,7 @@ const createPreprocessor = function (options) {
const setDefaultPreprocessor = function (config) {
debug('set default preprocessor')

const tsPath = resolve.typescript(config)

const tsPath = resolve.typescript(config.projectRoot)
const options = {
typescript: tsPath,
}
Expand Down
5 changes: 0 additions & 5 deletions packages/server/lib/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const keys = require('./util/keys')
const settings = require('./util/settings')
const specsUtil = require('./util/specs')
const { escapeFilenameInUrl } = require('./util/escape_filename')
const { registerTsNode } = require('./util/ts-node')

const localCwd = cwd()

Expand Down Expand Up @@ -100,10 +99,6 @@ class Project extends EE {

return scaffold.plugins(path.dirname(cfg.pluginsFile), cfg)
}
}).then((cfg) => {
registerTsNode(this.projectRoot)

return cfg
}).then((cfg) => {
return this._initPlugins(cfg, options)
.then((modifiedCfg) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
const resolve = require('resolve')
const env = require('../util/env')
const env = require('./env')
const debug = require('debug')('cypress:server:plugins')

module.exports = {
/**
* Resolves the path to 'typescript' module.
*
* @param {Config} cypress config object
* @param {projectRoot} path to the project root
* @returns {string|null} path if typescript exists, otherwise null
*/
typescript: (config) => {
if (env.get('CYPRESS_INTERNAL_NO_TYPESCRIPT') === '1') {
typescript: (projectRoot) => {
if (env.get('CYPRESS_INTERNAL_NO_TYPESCRIPT') === '1' || !projectRoot) {
return null
}

try {
const options = {
basedir: config.projectRoot,
}

if (!config.projectRoot) {
throw new Error('Config is missing projet root')
basedir: projectRoot,
}

debug('resolving typescript with options %o', options)
Expand Down
19 changes: 12 additions & 7 deletions packages/server/lib/util/ts-node.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
const debug = require('debug')('cypress:server:ts-node')
const path = require('path')
const tsnode = require('ts-node')
const resolve = require('resolve')
const resolve = require('./resolve')

const getTsNodeOptions = (tsPath) => {
const getTsNodeOptions = (tsPath, pluginsFile) => {
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),
transpileOnly: true, // transpile only (no type-check) for speed
}
}

const registerTsNode = (projectRoot) => {
const registerTsNode = (projectRoot, pluginsFile) => {
try {
const tsPath = resolve.sync('typescript', {
basedir: projectRoot,
})
const tsOptions = getTsNodeOptions(tsPath)
const tsPath = resolve.typescript(projectRoot)

if (!tsPath) return

const tsOptions = getTsNodeOptions(tsPath, pluginsFile)

debug('typescript path: %s', tsPath)
debug('registering project TS with options %o', tsOptions)
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"cli-table3": "0.5.1",
"coffeescript": "1.12.7",
"color-string": "1.5.3",
"command-exists": "1.2.8",
"command-exists": "1.2.9",
"common-tags": "1.8.0",
"compression": "1.7.4",
"content-type": "1.0.4",
Expand Down
7 changes: 7 additions & 0 deletions packages/server/test/e2e/1_typescript_plugins_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,11 @@ describe('e2e typescript in plugins file', function () {
project: Fixtures.projectPath('ts-proj-esmoduleinterop-true'),
})
})

// https://github.com/cypress-io/cypress/issues/8359
it('loads tsconfig.json from plugins directory', function () {
return e2e.exec(this, {
project: Fixtures.projectPath('ts-proj-tsconfig-in-plugins'),
})
})
})
2 changes: 1 addition & 1 deletion packages/server/test/integration/http_requests_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Project = require(`${root}lib/project`)
const Watchers = require(`${root}lib/watchers`)
const pluginsModule = require(`${root}lib/plugins`)
const preprocessor = require(`${root}lib/plugins/preprocessor`)
const resolve = require(`${root}lib/plugins/resolve`)
const resolve = require(`${root}lib/util/resolve`)
const fs = require(`${root}lib/util/fs`)
const glob = require(`${root}lib/util/glob`)
const CacheBuster = require(`${root}lib/util/cache_buster`)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"supportFile": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
it('passes', () => {
expect(true).to.be.true
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// this tests that the tsconfig.json is loaded from the plugins directory.
// if it isn't, the lack of "downlevelIteration" support will cause this to
// fail at runtime with "RangeError: Invalid array length"
[...Array(100).keys()].map((x) => `${x}`)

export default () => {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"downlevelIteration": true
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="cypress" />

import fn from './commonjs-export-function'
import * as fn from './commonjs-export-function'

// if esModuleInterop is forced to be true, this will error // with 'fn is
// not a function'. instead, we allow the tsconfig.json to determine the value
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
42 changes: 40 additions & 2 deletions packages/server/test/unit/plugins/child/run_plugins_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ require('../../../spec_helper')

const _ = require('lodash')
const snapshot = require('snap-shot-it')
const tsnode = require('ts-node')

const preprocessor = require(`${root}../../lib/plugins/child/preprocessor`)
const task = require(`${root}../../lib/plugins/child/task`)
const runPlugins = require(`${root}../../lib/plugins/child/run_plugins`)
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`)

Expand All @@ -31,8 +33,7 @@ describe('lib/plugins/child/run_plugins', () => {

afterEach(() => {
mockery.deregisterMock('plugins-file')

return mockery.deregisterSubstitute('plugins-file')
mockery.deregisterSubstitute('plugins-file')
})

it('sends error message if pluginsFile is missing', function () {
Expand Down Expand Up @@ -77,6 +78,43 @@ describe('lib/plugins/child/run_plugins', () => {
return snapshot(JSON.stringify(this.ipc.send.lastCall.args[3]))
})

describe('typescript registration', () => {
beforeEach(function () {
runPlugins.__reset()

this.register = sinon.stub(tsnode, 'register')
sinon.stub(resolve, 'typescript').returns('/path/to/typescript.js')
})

it('registers ts-node if typescript is installed', function () {
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')

expect(this.register).to.be.calledWith({
transpileOnly: true,
compiler: '/path/to/typescript.js',
dir: '/path/to/plugins',
compilerOptions: {
module: 'CommonJS',
},
})
})

it('only registers ts-node once', function () {
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')

expect(this.register).to.be.calledOnce
})

it('does not register ts-node if typescript is not installed', function () {
resolve.typescript.returns(null)

runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')

expect(this.register).not.to.be.called
})
})

describe('on \'load\' message', () => {
it('sends error if pluginsFile function rejects the promise', function (done) {
const err = new Error('foo')
Expand Down
52 changes: 0 additions & 52 deletions packages/server/test/unit/project_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ require('../spec_helper')
const mockedEnv = require('mocked-env')
const path = require('path')
const commitInfo = require('@cypress/commit-info')
const tsnode = require('ts-node')
const Fixtures = require('../support/helpers/fixtures')
const api = require(`${root}lib/api`)
const user = require(`${root}lib/user`)
Expand Down Expand Up @@ -316,57 +315,6 @@ This option will not have an effect in Some-other-name. Tests that rely on web s
expect(config).ok
})
})

describe('out-of-the-box typescript setup', () => {
const tsProjPath = Fixtures.projectPath('ts-installed')
// Root path is used because resolve finds server typescript path when we use a project under `suppert/projects` folder.
const rootPath = path.join(__dirname, '../../../../..')
const projTsPath = path.join(tsProjPath, 'node_modules/typescript/index.js')

let cfg

beforeEach(() => {
return config.get(tsProjPath, {})
.then((c) => {
cfg = c
})
})

const setupProject = (typescript, projectRoot) => {
const proj = new Project(projectRoot)

sinon.stub(proj, 'watchSettingsAndStartWebsockets').resolves()
sinon.stub(proj, 'checkSupportFile').resolves()
sinon.stub(proj, 'scaffold').resolves()
sinon.stub(proj, 'getConfig').resolves({ ...cfg, typescript })

const register = sinon.stub(tsnode, 'register')

return { proj, register }
}

it('ts installed', () => {
const { proj, register } = setupProject('default', tsProjPath)

return proj.open().then(() => {
expect(register).to.be.calledWith({
transpileOnly: true,
compiler: projTsPath,
compilerOptions: {
module: 'CommonJS',
},
})
})
})

it('ts not installed', () => {
const { proj, register } = setupProject('default', rootPath)

return proj.open().then(() => {
expect(register).not.called
})
})
})
})

context('#close', () => {
Expand Down
Loading

0 comments on commit ec76ab2

Please sign in to comment.