mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-07 06:59:49 -06:00
fix: restart dev-server on config change (#21212)
* fix: restart dev-server on config change * close dev server before cp is spawned * fix test that is failing... not sure why * cleanup unsued close events * remove wait * add back in close for unit tests Co-authored-by: Tim Griesser <tgriesser10@gmail.com>
This commit is contained in:
@@ -33,6 +33,7 @@ export async function devServer (config: ViteDevServerConfig): Promise<Cypress.R
|
||||
|
||||
return {
|
||||
port,
|
||||
// Close is for unit testing only. We kill this child process which will handle the closing of the server
|
||||
close (cb) {
|
||||
return server.close().then(() => cb?.()).catch(cb)
|
||||
},
|
||||
|
||||
@@ -76,6 +76,7 @@ export function devServer (devServerConfig: WebpackDevServerConfig): Promise<Cyp
|
||||
|
||||
resolve({
|
||||
port,
|
||||
// Close is for unit testing only. We kill this child process which will handle the closing of the server
|
||||
close: (done) => {
|
||||
srv.close((err) => {
|
||||
if (err) {
|
||||
@@ -101,7 +102,8 @@ export function devServer (devServerConfig: WebpackDevServerConfig): Promise<Cyp
|
||||
|
||||
resolve({
|
||||
port: result.server.options.port as number,
|
||||
close: (done) => {
|
||||
// Close is for unit testing only. We kill this child process which will handle the closing of the server
|
||||
close: async (done) => {
|
||||
debug('closing dev server')
|
||||
result.server.stop().then(() => done?.()).catch(done).finally(() => {
|
||||
debug('closed dev server')
|
||||
|
||||
@@ -4,156 +4,192 @@ import { getPathForPlatform } from '../../src/paths'
|
||||
import { snapshotAUTPanel } from './support/snapshot-aut-panel'
|
||||
|
||||
describe('Cypress In Cypress CT', { viewportWidth: 1500, defaultCommandTimeout: 10000 }, () => {
|
||||
beforeEach(() => {
|
||||
cy.scaffoldProject('cypress-in-cypress')
|
||||
cy.findBrowsers()
|
||||
cy.openProject('cypress-in-cypress')
|
||||
cy.startAppServer('component')
|
||||
})
|
||||
|
||||
it('test component', () => {
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.location().should((location) => {
|
||||
expect(location.hash).to.contain('TestComponent.spec')
|
||||
context('default config', () => {
|
||||
beforeEach(() => {
|
||||
cy.scaffoldProject('cypress-in-cypress')
|
||||
cy.findBrowsers()
|
||||
cy.openProject('cypress-in-cypress')
|
||||
cy.startAppServer('component')
|
||||
})
|
||||
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
it('test component', () => {
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.location().should((location) => {
|
||||
expect(location.hash).to.contain('TestComponent.spec')
|
||||
})
|
||||
|
||||
cy.findByTestId('aut-url').should('not.exist')
|
||||
cy.findByTestId('select-browser').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
cy.contains('Canary').should('be.visible')
|
||||
cy.findByTestId('viewport').click()
|
||||
cy.findByTestId('aut-url').should('not.exist')
|
||||
cy.findByTestId('select-browser').click()
|
||||
|
||||
snapshotAUTPanel('browsers open')
|
||||
cy.contains('Canary').should('be.hidden')
|
||||
cy.contains('The viewport determines the width and height of your application. By default the viewport will be 500px by 500px for Component Testing unless specified by a cy.viewport command.')
|
||||
.should('be.visible')
|
||||
cy.contains('Canary').should('be.visible')
|
||||
cy.findByTestId('viewport').click()
|
||||
|
||||
snapshotAUTPanel('viewport info open')
|
||||
snapshotAUTPanel('browsers open')
|
||||
cy.contains('Canary').should('be.hidden')
|
||||
cy.contains('The viewport determines the width and height of your application. By default the viewport will be 500px by 500px for Component Testing unless specified by a cy.viewport command.')
|
||||
.should('be.visible')
|
||||
|
||||
cy.get('body').click()
|
||||
snapshotAUTPanel('viewport info open')
|
||||
|
||||
cy.findByTestId('playground-activator').click()
|
||||
cy.findByTestId('playground-selector').clear().type('[data-cy-root]')
|
||||
cy.get('body').click()
|
||||
|
||||
snapshotAUTPanel('cy.get selector')
|
||||
cy.findByTestId('playground-activator').click()
|
||||
cy.findByTestId('playground-selector').clear().type('[data-cy-root]')
|
||||
|
||||
cy.findByTestId('playground-num-elements').contains('1 Match')
|
||||
snapshotAUTPanel('cy.get selector')
|
||||
|
||||
cy.window().then((win) => cy.spy(win.console, 'log'))
|
||||
cy.findByTestId('playground-print').click().window().then((win) => {
|
||||
expect(win.console.log).to.have.been.calledWith('%cCommand: ', 'font-weight: bold', 'cy.get(\'[data-cy-root]\')')
|
||||
cy.findByTestId('playground-num-elements').contains('1 Match')
|
||||
|
||||
cy.window().then((win) => cy.spy(win.console, 'log'))
|
||||
cy.findByTestId('playground-print').click().window().then((win) => {
|
||||
expect(win.console.log).to.have.been.calledWith('%cCommand: ', 'font-weight: bold', 'cy.get(\'[data-cy-root]\')')
|
||||
})
|
||||
|
||||
cy.findByLabelText('Selector Methods').click()
|
||||
cy.findByRole('menuitem', { name: 'cy.contains' }).click()
|
||||
|
||||
cy.findByTestId('playground-selector').clear().type('Component Test')
|
||||
|
||||
snapshotAUTPanel('cy.contains selector')
|
||||
|
||||
cy.findByTestId('playground-num-elements').contains('1 Match')
|
||||
})
|
||||
|
||||
cy.findByLabelText('Selector Methods').click()
|
||||
cy.findByRole('menuitem', { name: 'cy.contains' }).click()
|
||||
it('navigation between specs and other parts of the app works', () => {
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
cy.findByTestId('playground-selector').clear().type('Component Test')
|
||||
// go to Settings page and back to spec runner
|
||||
cy.contains('a', 'Settings').click()
|
||||
cy.contains(defaultMessages.settingsPage.device.title).should('be.visible')
|
||||
cy.contains('a', 'Specs').click()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
snapshotAUTPanel('cy.contains selector')
|
||||
// go to Runs page and back to spec runner
|
||||
cy.contains('a', 'Runs').click()
|
||||
cy.contains(defaultMessages.runs.connect.title).should('be.visible')
|
||||
cy.contains('a', 'Specs').click()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
})
|
||||
|
||||
cy.findByTestId('playground-num-elements').contains('1 Match')
|
||||
})
|
||||
it('redirects to the specs list with error if a spec is not found', () => {
|
||||
cy.visitApp()
|
||||
const { noSpecErrorTitle, noSpecErrorIntro, noSpecErrorExplainer } = defaultMessages.specPage
|
||||
const badFilePath = 'src/DoesNotExist.spec.js'
|
||||
|
||||
it('navigation between specs and other parts of the app works', () => {
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
// go to Settings page and back to spec runner
|
||||
cy.contains('a', 'Settings').click()
|
||||
cy.contains(defaultMessages.settingsPage.device.title).should('be.visible')
|
||||
cy.contains('a', 'Specs').click()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
// go to Runs page and back to spec runner
|
||||
cy.contains('a', 'Runs').click()
|
||||
cy.contains(defaultMessages.runs.connect.title).should('be.visible')
|
||||
cy.contains('a', 'Specs').click()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
})
|
||||
|
||||
it('redirects to the specs list with error if a spec is not found', () => {
|
||||
cy.visitApp()
|
||||
const { noSpecErrorTitle, noSpecErrorIntro, noSpecErrorExplainer } = defaultMessages.specPage
|
||||
const badFilePath = 'src/DoesNotExist.spec.js'
|
||||
|
||||
cy.visitApp(`/specs/runner?file=${badFilePath}`)
|
||||
cy.contains(noSpecErrorTitle).should('be.visible')
|
||||
cy.contains(noSpecErrorIntro).should('be.visible')
|
||||
cy.contains(noSpecErrorExplainer).should('be.visible')
|
||||
cy.contains(getPathForPlatform(badFilePath)).should('be.visible')
|
||||
cy.location()
|
||||
.its('href')
|
||||
.should('eq', 'http://localhost:4455/__/#/specs')
|
||||
|
||||
// should clear after reload
|
||||
cy.reload()
|
||||
cy.contains(noSpecErrorTitle).should('not.exist')
|
||||
})
|
||||
|
||||
it('redirects to the specs list with error if an open spec is not found when specs list updates', () => {
|
||||
const { noSpecErrorTitle, noSpecErrorIntro, noSpecErrorExplainer } = defaultMessages.specPage
|
||||
|
||||
const goodFilePath = 'src/TestComponent.spec.jsx'
|
||||
|
||||
cy.visitApp(`/specs/runner?file=${goodFilePath}`)
|
||||
|
||||
cy.contains('renders the test component').should('be.visible')
|
||||
|
||||
cy.withCtx((ctx, o) => {
|
||||
ctx.actions.project.setSpecs(ctx.project.specs.filter((spec) => !spec.absolute.includes(o.path)))
|
||||
}, { path: goodFilePath }).then(() => {
|
||||
cy.visitApp(`/specs/runner?file=${badFilePath}`)
|
||||
cy.contains(noSpecErrorTitle).should('be.visible')
|
||||
cy.contains(noSpecErrorIntro).should('be.visible')
|
||||
cy.contains(noSpecErrorExplainer).should('be.visible')
|
||||
cy.contains(getPathForPlatform(goodFilePath)).should('be.visible')
|
||||
cy.contains(getPathForPlatform(badFilePath)).should('be.visible')
|
||||
cy.location()
|
||||
.its('href')
|
||||
.should('eq', 'http://localhost:4455/__/#/specs')
|
||||
|
||||
// should clear after reload
|
||||
cy.reload()
|
||||
cy.contains(noSpecErrorTitle).should('not.exist')
|
||||
})
|
||||
|
||||
it('redirects to the specs list with error if an open spec is not found when specs list updates', () => {
|
||||
const { noSpecErrorTitle, noSpecErrorIntro, noSpecErrorExplainer } = defaultMessages.specPage
|
||||
|
||||
const goodFilePath = 'src/TestComponent.spec.jsx'
|
||||
|
||||
cy.visitApp(`/specs/runner?file=${goodFilePath}`)
|
||||
|
||||
cy.contains('renders the test component').should('be.visible')
|
||||
|
||||
cy.withCtx((ctx, o) => {
|
||||
ctx.actions.project.setSpecs(ctx.project.specs.filter((spec) => !spec.absolute.includes(o.path)))
|
||||
}, { path: goodFilePath }).then(() => {
|
||||
cy.contains(noSpecErrorTitle).should('be.visible')
|
||||
cy.contains(noSpecErrorIntro).should('be.visible')
|
||||
cy.contains(noSpecErrorExplainer).should('be.visible')
|
||||
cy.contains(getPathForPlatform(goodFilePath)).should('be.visible')
|
||||
cy.location()
|
||||
.its('href')
|
||||
.should('eq', 'http://localhost:4455/__/#/specs')
|
||||
})
|
||||
})
|
||||
|
||||
it('browser picker in runner calls mutation with current spec path', () => {
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
cy.withCtx((ctx, o) => {
|
||||
o.sinon.stub(ctx.actions.app, 'setActiveBrowserById')
|
||||
o.sinon.stub(ctx.actions.project, 'launchProject').resolves()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="select-browser"]')
|
||||
.click()
|
||||
|
||||
cy.contains('Firefox')
|
||||
.click()
|
||||
|
||||
cy.withCtx((ctx, o) => {
|
||||
const browserId = (ctx.actions.app.setActiveBrowserById as SinonStub).args[0][0]
|
||||
const genId = ctx.fromId(browserId, 'Browser')
|
||||
|
||||
expect(ctx.actions.app.setActiveBrowserById).to.have.been.calledWith(browserId)
|
||||
expect(genId).to.eql('firefox-firefox-stable')
|
||||
expect(ctx.actions.project.launchProject).to.have.been.calledWith(
|
||||
ctx.coreData.currentTestingType, {}, o.sinon.match(new RegExp('cypress\-in\-cypress\/src\/TestComponent\.spec\.jsx$')),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('restarts dev server on config change', () => {
|
||||
cy.visitApp()
|
||||
|
||||
cy.withCtx(async (ctx, { testState, sinon }) => {
|
||||
sinon.stub(ctx._apis.projectApi.getDevServer(), 'close')
|
||||
const devServerReady =
|
||||
new Promise<void>((res) => {
|
||||
ctx._apis.projectApi.getDevServer().emitter.on('dev-server:compile:success', () => res())
|
||||
})
|
||||
|
||||
testState.originalCypressConfig = await ctx.file.readFileInProject('cypress.config.js')
|
||||
const newCypressConfig = testState.originalCypressConfig.replace(`webpackConfig: require('./webpack.config.js')`, `webpackConfig: {}`)
|
||||
|
||||
await ctx.actions.file.writeFileInProject('cypress.config.js', newCypressConfig)
|
||||
await devServerReady
|
||||
})
|
||||
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('.failed > .num').should('contain', 1)
|
||||
|
||||
cy.withCtx(async (ctx, { testState }) => {
|
||||
await ctx.actions.file.writeFileInProject('cypress.config.js', testState.originalCypressConfig)
|
||||
})
|
||||
|
||||
cy.get('.passed > .num').should('contain', 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('browser picker in runner calls mutation with current spec path', () => {
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
cy.get('[data-model-state="passed"]').should('contain', 'renders the test component')
|
||||
|
||||
cy.withCtx((ctx, o) => {
|
||||
o.sinon.stub(ctx.actions.app, 'setActiveBrowserById')
|
||||
o.sinon.stub(ctx.actions.project, 'launchProject').resolves()
|
||||
context('custom config', () => {
|
||||
beforeEach(() => {
|
||||
cy.scaffoldProject('cypress-in-cypress')
|
||||
cy.findBrowsers()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="select-browser"]')
|
||||
.click()
|
||||
it('set the correct viewport values from CLI', () => {
|
||||
cy.openProject('cypress-in-cypress', ['--config', 'viewportWidth=333,viewportHeight=333'])
|
||||
cy.startAppServer('component')
|
||||
|
||||
cy.contains('Firefox')
|
||||
.click()
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
|
||||
cy.withCtx((ctx, o) => {
|
||||
const browserId = (ctx.actions.app.setActiveBrowserById as SinonStub).args[0][0]
|
||||
const genId = ctx.fromId(browserId, 'Browser')
|
||||
|
||||
expect(ctx.actions.app.setActiveBrowserById).to.have.been.calledWith(browserId)
|
||||
expect(genId).to.eql('firefox-firefox-stable')
|
||||
expect(ctx.actions.project.launchProject).to.have.been.calledWith(
|
||||
ctx.coreData.currentTestingType, {}, o.sinon.match(new RegExp('cypress\-in\-cypress\/src\/TestComponent\.spec\.jsx$')),
|
||||
)
|
||||
cy.get('#unified-runner').should('have.css', 'width', '333px')
|
||||
cy.get('#unified-runner').should('have.css', 'height', '333px')
|
||||
})
|
||||
})
|
||||
|
||||
it('set the correct viewport values from CLI', () => {
|
||||
cy.openProject('cypress-in-cypress', ['--config', 'viewportWidth=333,viewportHeight=333'])
|
||||
cy.startAppServer('component')
|
||||
|
||||
cy.visitApp()
|
||||
cy.contains('TestComponent.spec').click()
|
||||
|
||||
cy.get('#unified-runner').should('have.css', 'width', '333px')
|
||||
cy.get('#unified-runner').should('have.css', 'height', '333px')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,10 +2,9 @@ describe('Spec List - Git Status', () => {
|
||||
beforeEach(() => {
|
||||
cy.scaffoldProject('cypress-in-cypress')
|
||||
.then((projectPath) => {
|
||||
cy.task('initGitRepoForTestProject', projectPath)
|
||||
cy.wait(500)
|
||||
cy.openProject('cypress-in-cypress')
|
||||
cy.startAppServer('e2e')
|
||||
cy.task('initGitRepoForTestProject', projectPath)
|
||||
cy.visitApp()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CodeGenType, MutationSetProjectPreferencesArgs, NexusGenObjects, NexusGenUnions, TestingTypeEnum } from '@packages/graphql/src/gen/nxs.gen'
|
||||
import type { InitializeProjectOptions, FoundBrowser, FoundSpec, LaunchOpts, OpenProjectLaunchOptions, Preferences, TestingType, ReceivedCypressOptions, AddProject } from '@packages/types'
|
||||
import type { InitializeProjectOptions, FoundBrowser, FoundSpec, LaunchOpts, OpenProjectLaunchOptions, Preferences, TestingType, ReceivedCypressOptions, AddProject, FullConfig } from '@packages/types'
|
||||
import execa from 'execa'
|
||||
import path from 'path'
|
||||
import assert from 'assert'
|
||||
@@ -34,7 +34,10 @@ export interface ProjectApiShape {
|
||||
getCurrentProjectSavedState(): {} | undefined
|
||||
setPromptShown(slug: string): void
|
||||
getDevServer (): {
|
||||
updateSpecs: (specs: FoundSpec[]) => void
|
||||
updateSpecs(specs: FoundSpec[]): void
|
||||
start(options: {specs: Cypress.Spec[], config: FullConfig}): Promise<{port: number}>
|
||||
close(): void
|
||||
emitter: EventEmitter
|
||||
}
|
||||
isListening: (url: string) => Promise<void>
|
||||
}
|
||||
|
||||
@@ -219,6 +219,26 @@ export class ProjectLifecycleManager {
|
||||
this.ctx.emitter.toLaunchpad()
|
||||
},
|
||||
onFinalConfigLoaded: async (finalConfig: FullConfig) => {
|
||||
if (this._currentTestingType && finalConfig.specPattern) {
|
||||
await this.ctx.actions.project.setSpecsFoundBySpecPattern({
|
||||
path: this.projectRoot,
|
||||
testingType: this._currentTestingType,
|
||||
specPattern: this.ctx.modeOptions.spec || finalConfig.specPattern,
|
||||
excludeSpecPattern: finalConfig.excludeSpecPattern,
|
||||
additionalIgnorePattern: finalConfig.additionalIgnorePattern,
|
||||
})
|
||||
}
|
||||
|
||||
if (this._currentTestingType === 'component') {
|
||||
const devServerOptions = await this.ctx._apis.projectApi.getDevServer().start({ specs: this.ctx.project.specs, config: finalConfig })
|
||||
|
||||
if (!devServerOptions?.port) {
|
||||
this.ctx.onError(getError('CONFIG_FILE_DEV_SERVER_INVALID_RETURN', devServerOptions))
|
||||
}
|
||||
|
||||
finalConfig.baseUrl = `http://localhost:${devServerOptions?.port}`
|
||||
}
|
||||
|
||||
this._cachedFullConfig = finalConfig
|
||||
|
||||
// This happens automatically with openProjectCreate in run mode
|
||||
@@ -230,16 +250,6 @@ export class ProjectLifecycleManager {
|
||||
|
||||
await this.setInitialActiveBrowser()
|
||||
|
||||
if (this._currentTestingType && finalConfig.specPattern) {
|
||||
await this.ctx.actions.project.setSpecsFoundBySpecPattern({
|
||||
path: this.projectRoot,
|
||||
testingType: this._currentTestingType,
|
||||
specPattern: this.ctx.modeOptions.spec || finalConfig.specPattern,
|
||||
excludeSpecPattern: finalConfig.excludeSpecPattern,
|
||||
additionalIgnorePattern: finalConfig.additionalIgnorePattern,
|
||||
})
|
||||
}
|
||||
|
||||
this._pendingInitialize?.resolve(finalConfig)
|
||||
this.ctx.emitter.configChange()
|
||||
},
|
||||
@@ -297,6 +307,12 @@ export class ProjectLifecycleManager {
|
||||
await this.initializeConfig()
|
||||
|
||||
if (this._currentTestingType && this.isTestingTypeConfigured(this._currentTestingType)) {
|
||||
if (this._currentTestingType === 'component') {
|
||||
// Since we refresh the dev-server on config changes, we need to close it and clean up it's listeners
|
||||
// before we can start a new one. This needs to happen before we have registered the events of the child process
|
||||
this.ctx._apis.projectApi.getDevServer().close()
|
||||
}
|
||||
|
||||
this._configManager.loadTestingType()
|
||||
} else {
|
||||
this.setAndLoadCurrentTestingType(null)
|
||||
|
||||
47
packages/errors/__snapshot-html__/CONFIG_FILE_DEV_SERVER_INVALID_RETURN.html
generated
Normal file
47
packages/errors/__snapshot-html__/CONFIG_FILE_DEV_SERVER_INVALID_RETURN.html
generated
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: "Courier Prime", Courier, monospace;
|
||||
padding: 0 1em;
|
||||
line-height: 1.4;
|
||||
color: #eee;
|
||||
background-color: #111;
|
||||
}
|
||||
pre {
|
||||
padding: 0 0;
|
||||
margin: 0 0;
|
||||
font-family: "Courier Prime", Courier, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 5px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body><pre><span style="color:#e05561">The returned value of the <span style="color:#e5e510">devServer<span style="color:#e05561"> function is not valid.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">The returned value must be an object with a <span style="color:#e5e510">port<span style="color:#e05561"> property of the dev-server that is running.<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Instead, we saw:<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#de73ff">{}<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">Learn more: https://on.cypress.io/dev-server<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -1370,6 +1370,20 @@ export const AllCypressErrors = {
|
||||
`
|
||||
},
|
||||
|
||||
CONFIG_FILE_DEV_SERVER_INVALID_RETURN: (devServerOptions: any) => {
|
||||
return errTemplate`
|
||||
The returned value of the ${fmt.highlight('devServer')} function is not valid.
|
||||
|
||||
The returned value must be an object with a ${fmt.highlight('port')} property of the dev-server that is running.
|
||||
|
||||
Instead, we saw:
|
||||
|
||||
${fmt.stringify(devServerOptions)}
|
||||
|
||||
Learn more: https://on.cypress.io/dev-server
|
||||
`
|
||||
},
|
||||
|
||||
UNEXPECTED_MUTATION_ERROR: (mutationField: string, args: any, err: Error) => {
|
||||
return errTemplate`
|
||||
An unexpected internal error occurred while executing the ${fmt.highlight(mutationField)} operation with payload:
|
||||
|
||||
@@ -1051,6 +1051,11 @@ describe('visual error templates', () => {
|
||||
default: ['/path/to/plugins/file.js'],
|
||||
}
|
||||
},
|
||||
CONFIG_FILE_DEV_SERVER_INVALID_RETURN: () => {
|
||||
return {
|
||||
default: [{}],
|
||||
}
|
||||
},
|
||||
PLUGINS_RUN_EVENT_ERROR: () => {
|
||||
return {
|
||||
default: ['before:spec', makeErr()],
|
||||
|
||||
@@ -513,6 +513,7 @@ enum ErrorTypeEnum {
|
||||
CHROME_WEB_SECURITY_NOT_SUPPORTED
|
||||
COMPONENT_FOLDER_REMOVED
|
||||
CONFIG_FILES_LANGUAGE_CONFLICT
|
||||
CONFIG_FILE_DEV_SERVER_INVALID_RETURN
|
||||
CONFIG_FILE_DEV_SERVER_IS_NOT_VALID
|
||||
CONFIG_FILE_INVALID_DEV_START_EVENT
|
||||
CONFIG_FILE_INVALID_ROOT_CONFIG
|
||||
|
||||
@@ -266,15 +266,7 @@ export class OpenProject {
|
||||
})
|
||||
|
||||
try {
|
||||
const cfg = await this.projectBase.initializeConfig()
|
||||
|
||||
await this._ctx.actions.project.setSpecsFoundBySpecPattern({
|
||||
path,
|
||||
testingType,
|
||||
specPattern: options.spec || cfg.specPattern,
|
||||
excludeSpecPattern: cfg.excludeSpecPattern,
|
||||
additionalIgnorePattern: cfg.additionalIgnorePattern,
|
||||
})
|
||||
await this.projectBase.initializeConfig()
|
||||
|
||||
await this.projectBase.open()
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -19,12 +19,6 @@ plugins.registerHandler((ipc) => {
|
||||
ipc.on('dev-server:compile:success', ({ specFile } = {}) => {
|
||||
baseEmitter.emit('dev-server:compile:success', { specFile })
|
||||
})
|
||||
|
||||
baseEmitter.on('dev-server:close', () => {
|
||||
debug('base emitter plugin close event')
|
||||
|
||||
ipc.send('dev-server:close')
|
||||
})
|
||||
})
|
||||
|
||||
// for simpler stubbing from unit tests
|
||||
@@ -45,8 +39,6 @@ const API = {
|
||||
|
||||
close () {
|
||||
debug('close dev-server')
|
||||
baseEmitter.emit('close')
|
||||
|
||||
baseEmitter.removeAllListeners()
|
||||
},
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Automation } from './automation'
|
||||
import browsers from './browsers'
|
||||
import * as config from './config'
|
||||
import * as errors from './errors'
|
||||
import devServer from './plugins/dev-server'
|
||||
import preprocessor from './plugins/preprocessor'
|
||||
import runEvents from './plugins/run_events'
|
||||
import { checkSupportFile } from './project_utils'
|
||||
@@ -156,12 +155,6 @@ export class ProjectBase<TServer extends Server> extends EE {
|
||||
|
||||
this._server = this.createServer(this.testingType)
|
||||
|
||||
const { ctDevServerPort } = await this.initializeSpecsAndDevServer(cfg)
|
||||
|
||||
if (this.testingType === 'component') {
|
||||
cfg.baseUrl = `http://localhost:${ctDevServerPort}`
|
||||
}
|
||||
|
||||
const [port, warning] = await this._server.open(cfg, {
|
||||
getCurrentBrowser: () => this.browser,
|
||||
getSpec: () => this.spec,
|
||||
@@ -266,7 +259,6 @@ export class ProjectBase<TServer extends Server> extends EE {
|
||||
|
||||
__reset () {
|
||||
preprocessor.close()
|
||||
devServer.close()
|
||||
|
||||
process.chdir(localCwd)
|
||||
}
|
||||
@@ -308,46 +300,6 @@ export class ProjectBase<TServer extends Server> extends EE {
|
||||
options.onError(err)
|
||||
}
|
||||
|
||||
async initializeSpecsAndDevServer (updatedConfig: Cfg): Promise<{
|
||||
ctDevServerPort: number | undefined
|
||||
}> {
|
||||
const specs = this.ctx.project.specs || []
|
||||
|
||||
let ctDevServerPort: number | undefined
|
||||
|
||||
if (!this.ctx.currentProject) {
|
||||
throw new Error('Cannot set specs without current project')
|
||||
}
|
||||
|
||||
updatedConfig.specs = specs
|
||||
|
||||
if (this.testingType === 'component' && !this.options.skipPluginInitializeForTesting) {
|
||||
const { port } = await this.startCtDevServer(specs, updatedConfig)
|
||||
|
||||
ctDevServerPort = port
|
||||
}
|
||||
|
||||
return {
|
||||
ctDevServerPort,
|
||||
}
|
||||
}
|
||||
|
||||
async startCtDevServer (specs: Cypress.Cypress['spec'][], config: any) {
|
||||
// CT uses a dev-server to build the bundle.
|
||||
// We start the dev server here.
|
||||
const devServerOptions = await devServer.start({ specs, config })
|
||||
|
||||
if (!devServerOptions) {
|
||||
throw new Error([
|
||||
'It looks like nothing was returned from on(\'dev-server:start\', {here}).',
|
||||
'Make sure that the dev-server:start function returns an object.',
|
||||
'For example: on("dev-server:start", () => startWebpackDevServer({ webpackConfig }))',
|
||||
].join('\n'))
|
||||
}
|
||||
|
||||
return { port: devServerOptions.port }
|
||||
}
|
||||
|
||||
initializeReporter ({
|
||||
report,
|
||||
reporter,
|
||||
|
||||
@@ -14,5 +14,8 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// These tests should run quickly / fail quickly,
|
||||
// since we intentionally causing error states for testing
|
||||
defaultCommandTimeout: 1000
|
||||
}
|
||||
Reference in New Issue
Block a user