diff --git a/npm/webpack-dev-server/cypress/e2e/angular.cy.ts b/npm/webpack-dev-server/cypress/e2e/angular.cy.ts
index c0454de4dcf0..a67c61df8b5c 100644
--- a/npm/webpack-dev-server/cypress/e2e/angular.cy.ts
+++ b/npm/webpack-dev-server/cypress/e2e/angular.cy.ts
@@ -1,4 +1,4 @@
-//
+///
///
import type { ProjectFixtureDir } from '@tooling/system-tests/lib/fixtureDirs'
@@ -12,83 +12,106 @@ for (const project of WEBPACK_REACT) {
continue
}
- describe(`Working with ${project}`, () => {
+ context(project, () => {
beforeEach(() => {
cy.scaffoldProject(project)
cy.openProject(project)
- cy.startAppServer('component')
})
- it('should mount a passing test', () => {
- cy.visitApp()
- cy.contains('app.component.cy.ts').click()
- cy.waitForSpecToFinish({ passCount: 1 }, 60000)
+ describe('configuration handling', () => {
+ if (!['angular-13', 'angular-14'].includes(project)) {
+ it('should initialize with unsupported browserslist entries', () => {
+ // Create .browerslistrc that requests support for ES5
+ // Support was dropped in Angular CLI v15 so this should generate a warning message in that version and beyond
+ cy.withCtx(async (ctx) => {
+ await ctx.actions.file.writeFileInProject(
+ ctx.path.resolve('.browserslistrc'),
+ 'IE 11',
+ )
+ })
+
+ cy.startAppServer('component')
+ cy.visitApp()
+ })
+ }
+ })
- cy.get('li.command').first().within(() => {
- cy.get('.command-method').should('contain', 'mount')
- cy.get('.command-message').should('contain', 'AppComponent')
+ describe('test behaviors', () => {
+ beforeEach(() => {
+ cy.startAppServer('component')
})
- })
- it('should live-reload on src changes', () => {
- cy.visitApp()
- cy.contains('app.component.cy.ts').click()
- cy.waitForSpecToFinish({ passCount: 1 }, 60000)
+ it('should mount a passing test', () => {
+ cy.visitApp()
+ cy.contains('app.component.cy.ts').click()
+ cy.waitForSpecToFinish({ passCount: 1 }, 60000)
- cy.withCtx(async (ctx) => {
- await ctx.actions.file.writeFileInProject(
- ctx.path.join('src', 'app', 'app.component.html'),
- (await ctx.file.readFileInProject(ctx.path.join('src', 'app', 'app.component.html'))).replace('Hello World', 'Hello Cypress'),
- )
+ cy.get('li.command').first().within(() => {
+ cy.get('.command-method').should('contain', 'mount')
+ cy.get('.command-message').should('contain', 'AppComponent')
+ })
})
- cy.waitForSpecToFinish({ failCount: 1 }, 60000)
+ it('should live-reload on src changes', () => {
+ cy.visitApp()
+ cy.contains('app.component.cy.ts').click()
+ cy.waitForSpecToFinish({ passCount: 1 }, 60000)
+
+ cy.withCtx(async (ctx) => {
+ await ctx.actions.file.writeFileInProject(
+ ctx.path.join('src', 'app', 'app.component.html'),
+ (await ctx.file.readFileInProject(ctx.path.join('src', 'app', 'app.component.html'))).replace('Hello World', 'Hello Cypress'),
+ )
+ })
+
+ cy.waitForSpecToFinish({ failCount: 1 }, 60000)
- cy.withCtx(async (ctx) => {
- await ctx.actions.file.writeFileInProject(
- ctx.path.join('src', 'app', 'app.component.html'),
- (await ctx.file.readFileInProject(ctx.path.join('src', 'app', 'app.component.html'))).replace('Hello Cypress', 'Hello World'),
- )
+ cy.withCtx(async (ctx) => {
+ await ctx.actions.file.writeFileInProject(
+ ctx.path.join('src', 'app', 'app.component.html'),
+ (await ctx.file.readFileInProject(ctx.path.join('src', 'app', 'app.component.html'))).replace('Hello Cypress', 'Hello World'),
+ )
+ })
+
+ cy.waitForSpecToFinish({ passCount: 1 }, 60000)
})
- cy.waitForSpecToFinish({ passCount: 1 }, 60000)
- })
+ it('should show compilation errors on src changes', () => {
+ cy.visitApp()
- it('should show compilation errors on src changes', () => {
- cy.visitApp()
+ cy.contains('app.component.cy.ts').click()
+ cy.waitForSpecToFinish({ passCount: 1 }, 60000)
- cy.contains('app.component.cy.ts').click()
- cy.waitForSpecToFinish({ passCount: 1 }, 60000)
+ // Create compilation error
+ cy.withCtx(async (ctx) => {
+ const componentFilePath = ctx.path.join('src', 'app', 'app.component.ts')
- // Create compilation error
- cy.withCtx(async (ctx) => {
- const componentFilePath = ctx.path.join('src', 'app', 'app.component.ts')
+ await ctx.actions.file.writeFileInProject(
+ componentFilePath,
+ (await ctx.file.readFileInProject(componentFilePath)).replace('class', 'classaaaaa'),
+ )
+ })
- await ctx.actions.file.writeFileInProject(
- componentFilePath,
- (await ctx.file.readFileInProject(componentFilePath)).replace('class', 'classaaaaa'),
- )
+ // The test should fail and the stack trace should appear in the command log
+ cy.waitForSpecToFinish({ failCount: 1 }, 60000)
+ cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
+ cy.get('.test-err-code-frame').should('be.visible')
})
- // The test should fail and the stack trace should appear in the command log
- cy.waitForSpecToFinish({ failCount: 1 }, 60000)
- cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
- cy.get('.test-err-code-frame').should('be.visible')
- })
+ // TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23455
+ it('should detect new spec', { retries: 15 }, () => {
+ cy.visitApp()
- // TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23455
- it('should detect new spec', { retries: 15 }, () => {
- cy.visitApp()
+ cy.withCtx(async (ctx) => {
+ await ctx.actions.file.writeFileInProject(
+ ctx.path.join('src', 'app', 'new.component.cy.ts'),
+ await ctx.file.readFileInProject(ctx.path.join('src', 'app', 'app.component.cy.ts')),
+ )
+ })
- cy.withCtx(async (ctx) => {
- await ctx.actions.file.writeFileInProject(
- ctx.path.join('src', 'app', 'new.component.cy.ts'),
- await ctx.file.readFileInProject(ctx.path.join('src', 'app', 'app.component.cy.ts')),
- )
+ cy.contains('new.component.cy.ts').click()
+ cy.waitForSpecToFinish({ passCount: 1 }, 60000)
})
-
- cy.contains('new.component.cy.ts').click()
- cy.waitForSpecToFinish({ passCount: 1 }, 60000)
})
})
}
diff --git a/npm/webpack-dev-server/src/helpers/angularHandler.ts b/npm/webpack-dev-server/src/helpers/angularHandler.ts
index 1135be85d496..82c197569a29 100644
--- a/npm/webpack-dev-server/src/helpers/angularHandler.ts
+++ b/npm/webpack-dev-server/src/helpers/angularHandler.ts
@@ -6,8 +6,10 @@ import type { PresetHandlerResult, WebpackDevServerConfig } from '../devServer'
import { dynamicAbsoluteImport, dynamicImport } from '../dynamic-import'
import { sourceDefaultWebpackDependencies } from './sourceRelativeWebpackModules'
import debugLib from 'debug'
+import type { logging as AngularLogging } from '@angular-devkit/core'
-const debug = debugLib('cypress:webpack-dev-server:angularHandler')
+const debugPrefix = 'cypress:webpack-dev-server:angularHandler'
+const debug = debugLib(debugPrefix)
export type BuildOptions = Record
@@ -166,19 +168,21 @@ export async function getAngularCliModules (projectRoot: string) {
'@angular-devkit/build-angular/src/utils/webpack-browser-config.js',
'@angular-devkit/build-angular/src/webpack/configs/common.js',
'@angular-devkit/build-angular/src/webpack/configs/styles.js',
+ '@angular-devkit/core/src/index.js',
] as const
const [
{ generateBrowserWebpackConfigFromContext },
{ getCommonConfig },
{ getStylesConfig },
+ { logging },
] = await Promise.all(angularCLiModules.map((dep) => {
try {
const depPath = require.resolve(dep, { paths: [projectRoot] })
return dynamicAbsoluteImport(depPath)
} catch (e) {
- throw new Error(`Could not resolve "${dep}". Do you have "@angular-devkit/build-angular" installed?`)
+ throw new Error(`Could not resolve "${dep}". Do you have "@angular-devkit/build-angular" and "@angular-devkit/core" installed?`)
}
}))
@@ -186,6 +190,7 @@ export async function getAngularCliModules (projectRoot: string) {
generateBrowserWebpackConfigFromContext,
getCommonConfig,
getStylesConfig,
+ logging,
}
}
@@ -203,14 +208,13 @@ export async function getAngularJson (projectRoot: string): Promise
return JSON.parse(angularJson)
}
-function createFakeContext (projectRoot: string, defaultProjectConfig: Cypress.AngularDevServerProjectConfig) {
- const logger = {
- createChild: () => {
- return {
- warn: () => {},
- }
- },
- }
+function createFakeContext (projectRoot: string, defaultProjectConfig: Cypress.AngularDevServerProjectConfig, logging: typeof AngularLogging) {
+ const logger = new logging.Logger(debugPrefix)
+
+ // Proxy all logging calls through to the debug logger
+ logger.forEach((value: AngularLogging.LogEntry) => {
+ debug(JSON.stringify(value))
+ })
const context = {
target: {
@@ -239,6 +243,7 @@ async function getAngularCliWebpackConfig (devServerConfig: AngularWebpackDevSer
generateBrowserWebpackConfigFromContext,
getCommonConfig,
getStylesConfig,
+ logging,
} = await getAngularCliModules(projectRoot)
// normalize
@@ -248,7 +253,7 @@ async function getAngularCliWebpackConfig (devServerConfig: AngularWebpackDevSer
const buildOptions = getAngularBuildOptions(projectConfig.buildOptions, tsConfig)
- const context = createFakeContext(projectRoot, projectConfig)
+ const context = createFakeContext(projectRoot, projectConfig, logging)
const { config } = await generateBrowserWebpackConfigFromContext(
buildOptions,
diff --git a/system-tests/project-fixtures/angular/src/app/app.component.cy.ts b/system-tests/project-fixtures/angular/src/app/app.component.cy.ts
index 8356874b413e..aa1f8e4481f2 100644
--- a/system-tests/project-fixtures/angular/src/app/app.component.cy.ts
+++ b/system-tests/project-fixtures/angular/src/app/app.component.cy.ts
@@ -3,5 +3,5 @@ import { AppComponent } from './app.component'
it('should', () => {
cy.mount(AppComponent)
- cy.get('h1').contains('Hello World')
+ cy.get('h1').contains('Hello World', { timeout: 250 })
})