Skip to content

Commit

Permalink
Merge branch 'develop' into tgriesser/fix/change-typescript-detection…
Browse files Browse the repository at this point in the history
…-rules
  • Loading branch information
tgriesser authored Jun 2, 2022
2 parents 136d1f3 + e170d5d commit 704e0b3
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 104 deletions.
6 changes: 6 additions & 0 deletions packages/data-context/src/sources/ProjectDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@ export class ProjectDataSource {
}: FindSpecs<string[]>) {
this.stopSpecWatcher()

// Early return the spec watcher if we're in run mode, we do not want to
// init a lot of files watchers that are unneeded
if (this.ctx.isRunMode) {
return
}

const currentProject = this.ctx.currentProject

if (!currentProject) {
Expand Down
208 changes: 127 additions & 81 deletions packages/data-context/test/unit/sources/ProjectDataSource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,127 +447,173 @@ describe('startSpecWatcher', () => {

let ctx: DataContext

beforeEach(async () => {
ctx = createTestDataContext('run')

ctx.coreData.currentProject = projectRoot
})

afterEach(async () => {
sinon.restore()
})

it('throws if no current project defined', () => {
ctx.coreData.currentProject = null
describe('run mode', () => {
beforeEach(async () => {
ctx = createTestDataContext('run')

ctx.coreData.currentProject = projectRoot
})

it('early return specWatcher', () => {
const onStub = sinon.stub()

sinon.stub(chokidar, 'watch').callsFake(() => {
const mockWatcher = {
on: onStub,
close: () => ({ catch: () => {} }),
} as unknown

return mockWatcher as chokidar.FSWatcher
})

let handleFsChange

sinon.stub(_, 'debounce').callsFake((funcToDebounce) => {
handleFsChange = (() => funcToDebounce())

return handleFsChange as _.DebouncedFunc<any>
})

expect(() => {
return ctx.project.startSpecWatcher({
ctx.project.startSpecWatcher({
projectRoot,
testingType: 'e2e',
specPattern: ['**/*.{cy,spec}.{ts,js}'],
configSpecPattern: ['**/*.{cy,spec}.{ts,js}'],
excludeSpecPattern: ['**/ignore.spec.ts'],
additionalIgnorePattern: [],
additionalIgnorePattern: ['additional.ignore.cy.js'],
})
}).to.throw()
})

it('creates file watcher based on given config properties', () => {
const onStub = sinon.stub()
expect(_.debounce).to.have.not.been.called

sinon.stub(chokidar, 'watch').callsFake(() => {
const mockWatcher = {
on: onStub,
close: () => ({ catch: () => {} }),
} as unknown
expect(chokidar.watch).to.have.not.been.called

return mockWatcher as chokidar.FSWatcher
expect(onStub).to.have.not.been.called
})
})

let handleFsChange

sinon.stub(_, 'debounce').callsFake((funcToDebounce) => {
handleFsChange = (() => funcToDebounce())
describe('open mode', () => {
beforeEach(async () => {
ctx = createTestDataContext('open')

return handleFsChange as _.DebouncedFunc<any>
ctx.coreData.currentProject = projectRoot
})

ctx.project.startSpecWatcher({
projectRoot,
testingType: 'e2e',
specPattern: ['**/*.{cy,spec}.{ts,js}'],
configSpecPattern: ['**/*.{cy,spec}.{ts,js}'],
excludeSpecPattern: ['**/ignore.spec.ts'],
additionalIgnorePattern: ['additional.ignore.cy.js'],
it('throws if no current project defined', () => {
ctx.coreData.currentProject = null

expect(() => {
return ctx.project.startSpecWatcher({
projectRoot,
testingType: 'e2e',
specPattern: ['**/*.{cy,spec}.{ts,js}'],
configSpecPattern: ['**/*.{cy,spec}.{ts,js}'],
excludeSpecPattern: ['**/ignore.spec.ts'],
additionalIgnorePattern: [],
})
}).to.throw()
})

expect(_.debounce).to.have.been.calledWith(sinon.match.func, 250)
it('creates file watcher based on given config properties', () => {
const onStub = sinon.stub()

expect(chokidar.watch).to.have.been.calledWith('.', {
ignoreInitial: true,
cwd: projectRoot,
ignored: ['**/node_modules/**', '**/ignore.spec.ts', 'additional.ignore.cy.js'],
ignorePermissionErrors: true,
})
sinon.stub(chokidar, 'watch').callsFake(() => {
const mockWatcher = {
on: onStub,
close: () => ({ catch: () => {} }),
} as unknown

expect(onStub).to.have.been.calledWith('all', handleFsChange)
})
return mockWatcher as chokidar.FSWatcher
})

it('implements change handler with duplicate result handling', async () => {
const mockFoundSpecs = [
{ name: 'test-1.cy.js' },
{ name: 'test-2.cy.js' },
{ name: 'test-3.cy.js' },
] as FoundSpec[]
let handleFsChange

sinon.stub(ctx.project, 'findSpecs').resolves(mockFoundSpecs)
sinon.stub(ctx.actions.project, 'setSpecs')
sinon.stub(_, 'debounce').callsFake((funcToDebounce) => {
handleFsChange = (() => funcToDebounce())

sinon.stub(chokidar, 'watch').callsFake(() => {
const mockWatcher = {
on: () => {},
close: () => ({ catch: () => {} }),
} as unknown
return handleFsChange as _.DebouncedFunc<any>
})

return mockWatcher as chokidar.FSWatcher
})
ctx.project.startSpecWatcher({
projectRoot,
testingType: 'e2e',
specPattern: ['**/*.{cy,spec}.{ts,js}'],
configSpecPattern: ['**/*.{cy,spec}.{ts,js}'],
excludeSpecPattern: ['**/ignore.spec.ts'],
additionalIgnorePattern: ['additional.ignore.cy.js'],
})

let handleFsChange
expect(_.debounce).to.have.been.calledWith(sinon.match.func, 250)

sinon.stub(_, 'debounce').callsFake((funcToDebounce) => {
handleFsChange = (() => funcToDebounce())
expect(chokidar.watch).to.have.been.calledWith('.', {
ignoreInitial: true,
cwd: projectRoot,
ignored: ['**/node_modules/**', '**/ignore.spec.ts', 'additional.ignore.cy.js'],
ignorePermissionErrors: true,
})

return handleFsChange as _.DebouncedFunc<any>
expect(onStub).to.have.been.calledWith('all', handleFsChange)
})

const watchOptions: FindSpecs<string[]> = {
projectRoot,
testingType: 'e2e',
specPattern: ['**/*.{cy,spec}.{ts,js}'],
configSpecPattern: ['**/ignore.spec.ts'],
excludeSpecPattern: ['additional.ignore.cy.js'],
additionalIgnorePattern: [],
}
it('implements change handler with duplicate result handling', async () => {
const mockFoundSpecs = [
{ name: 'test-1.cy.js' },
{ name: 'test-2.cy.js' },
{ name: 'test-3.cy.js' },
] as FoundSpec[]

ctx.project.startSpecWatcher(watchOptions)
sinon.stub(ctx.project, 'findSpecs').resolves(mockFoundSpecs)
sinon.stub(ctx.actions.project, 'setSpecs')

// Set internal specs state to the stubbed found value to simulate irrelevant FS changes
ctx.project.setSpecs(mockFoundSpecs)
sinon.stub(chokidar, 'watch').callsFake(() => {
const mockWatcher = {
on: () => {},
close: () => ({ catch: () => {} }),
} as unknown

await handleFsChange()
return mockWatcher as chokidar.FSWatcher
})

expect(ctx.project.findSpecs).to.have.been.calledWith(watchOptions)
expect(ctx.actions.project.setSpecs).not.to.have.been.called
let handleFsChange

// Update internal specs state so that a change will be detected on next FS event
const updatedSpecs = [...mockFoundSpecs, { name: 'test-4.cy.js' }] as FoundSpec[]
sinon.stub(_, 'debounce').callsFake((funcToDebounce) => {
handleFsChange = (() => funcToDebounce())

ctx.project.setSpecs(updatedSpecs)
return handleFsChange as _.DebouncedFunc<any>
})

const watchOptions: FindSpecs<string[]> = {
projectRoot,
testingType: 'e2e',
specPattern: ['**/*.{cy,spec}.{ts,js}'],
configSpecPattern: ['**/ignore.spec.ts'],
excludeSpecPattern: ['additional.ignore.cy.js'],
additionalIgnorePattern: [],
}

ctx.project.startSpecWatcher(watchOptions)

await handleFsChange()
// Set internal specs state to the stubbed found value to simulate irrelevant FS changes
ctx.project.setSpecs(mockFoundSpecs)

expect(ctx.project.findSpecs).to.have.been.calledWith(watchOptions)
expect(ctx.actions.project.setSpecs).to.have.been.calledWith(mockFoundSpecs)
await handleFsChange()

expect(ctx.project.findSpecs).to.have.been.calledWith(watchOptions)
expect(ctx.actions.project.setSpecs).not.to.have.been.called

// Update internal specs state so that a change will be detected on next FS event
const updatedSpecs = [...mockFoundSpecs, { name: 'test-4.cy.js' }] as FoundSpec[]

ctx.project.setSpecs(updatedSpecs)

await handleFsChange()

expect(ctx.project.findSpecs).to.have.been.calledWith(watchOptions)
expect(ctx.actions.project.setSpecs).to.have.been.calledWith(mockFoundSpecs)
})
})
})

Expand Down
7 changes: 7 additions & 0 deletions packages/launchpad/cypress/e2e/scaffold-project.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ function scaffoldAndOpenE2EProject (opts: {
cy.contains('E2E Testing').click()
cy.contains('We added the following files to your project:')
cy.contains('Continue').click()
// Going through the loading of config
cy.get('[data-cy="loading-spinner"]')
cy.get('[data-cy="loading-spinner"]').should('not.exist')
// No errrors were encountered
cy.get('[data-testid="error-header"]').should('not.exist')
// Asserts that we've made it through the flow
cy.contains('Choose a Browser')
}

function scaffoldAndOpenCTProject (opts: {
Expand Down
17 changes: 5 additions & 12 deletions packages/server/lib/plugins/child/run_require_async_child.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require('graceful-fs').gracefulify(require('fs'))
const stripAnsi = require('strip-ansi')
const debug = require('debug')(`cypress:lifecycle:child:run_require_async_child:${process.pid}`)
const { pathToFileURL } = require('url')
const tsNodeUtil = require('./ts_node')
const util = require('../util')
const { RunPlugins } = require('./run_plugins')
Expand Down Expand Up @@ -97,12 +98,10 @@ function run (ipc, file, projectRoot) {
// 3a. Yes: Use bundleRequire
// 3b. No: Continue through to `await import(configFile)`
// 4. Use node's dynamic import to import the configFile
let originalError

try {
return require(file)
} catch (err) {
originalError = err
if (!err.stack.includes('[ERR_REQUIRE_ESM]') && !err.stack.includes('SyntaxError: Cannot use import statement outside a module')) {
throw err
}
Expand All @@ -124,16 +123,10 @@ function run (ipc, file, projectRoot) {
debug(`User doesn't have esbuild. Going to use native node imports.`)

// We cannot replace the initial `require` with `await import` because
// Certain modules cannot be dynamically imported. If this throws, however, we want
// to show the original error that was thrown, because that's ultimately the source of the problem
try {
return await import(file)
} catch (e) {
// If we aren't able to import the file at all, throw the original error, since that has more accurate information
// of what failed to begin with
debug('esbuild fallback for loading config failed, throwing original error. node import error: %o', e)
throw originalError
}
// Certain modules cannot be dynamically imported.

// pathToFileURL for windows interop: https://github.com/nodejs/node/issues/31710
return await import(pathToFileURL(file).href)
}

throw err
Expand Down
4 changes: 3 additions & 1 deletion packages/server/lib/video_capture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ export async function process (name, cname, videoCompression, ffmpegchaptersConf
debug('processing video from %s to %s video compression %o',
name, cname, videoCompression)

const command = ffmpeg()
const command = ffmpeg({
priority: 20,
})
.addOptions([
// These flags all serve to reduce initial buffering, especially important
// when dealing with very short videos (such as during component tests).
Expand Down
3 changes: 0 additions & 3 deletions system-tests/__snapshots__/config_modules_spec.ts.js

This file was deleted.

7 changes: 0 additions & 7 deletions system-tests/test/config_modules_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@ describe('cypress config with esm and cjs', function () {
spec: 'app.cy.js',
browser: 'chrome',
expectedExitCode: 1,
snapshot: true,
onStdout (stdout) {
expect(stdout).to.include('nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules')

// Need to make this stable b/c of filepaths, and snapshot: true is needed to invoke onStdout
return 'STDOUT_ERROR_VALIDATED'
},
})
})

Expand Down

0 comments on commit 704e0b3

Please sign in to comment.