Skip to content

Commit 11e54e1

Browse files
committed
fix(webpack-dev-server): handle polyfills array for upcoming angular 15 change
1 parent cc76ad5 commit 11e54e1

39 files changed

+6718
-2
lines changed

npm/webpack-dev-server/src/helpers/angularHandler.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,11 @@ export async function generateTsConfig (devServerConfig: AngularWebpackDevServer
124124
}
125125

126126
if (buildOptions.polyfills) {
127-
const polyfills = getProjectFilePath(buildOptions.polyfills)
127+
const polyfills = Array.isArray(buildOptions.polyfills)
128+
? buildOptions.polyfills.filter((p: string) => devServerConfig.options?.projectConfig.sourceRoot && p.startsWith(devServerConfig.options?.projectConfig.sourceRoot))
129+
: [buildOptions.polyfills]
128130

129-
includePaths.push(polyfills)
131+
includePaths.push(...polyfills.map((p: string) => getProjectFilePath(p)))
130132
}
131133

132134
const cypressTypes = getProjectFilePath('node_modules', 'cypress', 'types', 'index.d.ts')

npm/webpack-dev-server/test/handlers/angularHandler.spec.ts

+27
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,33 @@ describe('angularHandler', function () {
7777
expectLoadsAngularBuildOptions(buildOptions)
7878
})
7979

80+
it('sources the config from angular-15', async () => {
81+
const projectRoot = await scaffoldMigrationProject('angular-15')
82+
83+
process.chdir(projectRoot)
84+
85+
const devServerConfig = {
86+
cypressConfig: {
87+
projectRoot,
88+
specPattern: 'src/**/*.cy.ts',
89+
} as Cypress.PluginConfigOptions,
90+
framework: 'angular',
91+
} as AngularWebpackDevServerConfig
92+
93+
const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig)
94+
95+
expect(webpackConfig).to.exist
96+
expect((webpackConfig?.entry as any).main).to.be.undefined
97+
expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular'))
98+
99+
const { buildOptions } = await expectNormalizeProjectConfig(projectRoot)
100+
101+
await expectLoadsAngularJson(projectRoot)
102+
await expectLoadsAngularCLiModules(projectRoot)
103+
await expectGeneratesTsConfig(devServerConfig, buildOptions)
104+
expectLoadsAngularBuildOptions(buildOptions)
105+
})
106+
80107
it('allows custom project config', async () => {
81108
const customProjectConfig = {
82109
root: '',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3+
"cli": {
4+
"analytics": false
5+
},
6+
"version": 1,
7+
"newProjectRoot": "projects",
8+
"projects": {
9+
"angular": {
10+
"projectType": "application",
11+
"schematics": {
12+
"@schematics/angular:component": {
13+
"style": "scss"
14+
},
15+
"@schematics/angular:application": {
16+
"strict": true
17+
}
18+
},
19+
"root": "",
20+
"sourceRoot": "src",
21+
"prefix": "app",
22+
"architect": {
23+
"build": {
24+
"builder": "@angular-devkit/build-angular:browser",
25+
"options": {
26+
"outputPath": "dist/angular",
27+
"index": "src/index.html",
28+
"main": "src/main.ts",
29+
"polyfills": "src/polyfills.ts",
30+
"tsConfig": "tsconfig.app.json",
31+
"inlineStyleLanguage": "scss",
32+
"assets": [
33+
"src/favicon.ico",
34+
"src/assets"
35+
],
36+
"styles": [
37+
"src/styles.scss"
38+
],
39+
"scripts": []
40+
},
41+
"configurations": {
42+
"production": {
43+
"budgets": [
44+
{
45+
"type": "initial",
46+
"maximumWarning": "500kb",
47+
"maximumError": "1mb"
48+
},
49+
{
50+
"type": "anyComponentStyle",
51+
"maximumWarning": "2kb",
52+
"maximumError": "4kb"
53+
}
54+
],
55+
"fileReplacements": [
56+
{
57+
"replace": "src/environments/environment.ts",
58+
"with": "src/environments/environment.prod.ts"
59+
}
60+
],
61+
"outputHashing": "all"
62+
},
63+
"development": {
64+
"buildOptimizer": false,
65+
"optimization": false,
66+
"vendorChunk": true,
67+
"extractLicenses": false,
68+
"sourceMap": true,
69+
"namedChunks": true
70+
}
71+
},
72+
"defaultConfiguration": "production"
73+
},
74+
"serve": {
75+
"builder": "@angular-devkit/build-angular:dev-server",
76+
"configurations": {
77+
"production": {
78+
"browserTarget": "angular:build:production"
79+
},
80+
"development": {
81+
"browserTarget": "angular:build:development"
82+
}
83+
},
84+
"defaultConfiguration": "development"
85+
},
86+
"extract-i18n": {
87+
"builder": "@angular-devkit/build-angular:extract-i18n",
88+
"options": {
89+
"browserTarget": "angular:build"
90+
}
91+
}
92+
}
93+
}
94+
},
95+
"defaultProject": "angular"
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { defineConfig } from 'cypress'
2+
3+
export default defineConfig({
4+
component: {
5+
devServer: {
6+
framework: 'angular',
7+
bundler: 'webpack',
8+
webpackConfig: {
9+
resolve: {
10+
alias: {
11+
'@angular/common': require.resolve('@angular/common'),
12+
'@angular/core/testing': require.resolve('@angular/core/testing'),
13+
'@angular/core': require.resolve('@angular/core'),
14+
'@angular/platform-browser/testing': require.resolve('@angular/platform-browser/testing'),
15+
'@angular/platform-browser': require.resolve('@angular/platform-browser'),
16+
'@angular/platform-browser-dynamic/testing': require.resolve('@angular/platform-browser-dynamic/testing'),
17+
'@angular/platform-browser-dynamic': require.resolve('@angular/platform-browser-dynamic'),
18+
'zone.js/testing': require.resolve('zone.js/dist/zone-testing'),
19+
'zone.js': require.resolve('zone.js'),
20+
}
21+
}
22+
}
23+
},
24+
specPattern: 'src/**/*.cy.ts'
25+
},
26+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
7+
<title>Components App</title>
8+
</head>
9+
<body>
10+
<div data-cy-root></div>
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { mount } from 'cypress/angular'
2+
3+
declare global {
4+
namespace Cypress {
5+
interface Chainable {
6+
mount: typeof mount
7+
}
8+
}
9+
}
10+
11+
12+
Cypress.Commands.add('mount', mount);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { AppComponent } from './app.component'
2+
3+
it('should', () => {
4+
cy.mount(AppComponent)
5+
cy.get('h1').contains('Hello World')
6+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello World your app is running!</h1>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
h1 {
2+
color: red
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Component } from '@angular/core'
2+
3+
@Component({
4+
selector: 'app-root',
5+
templateUrl: './app.component.html',
6+
styleUrls: ['./app.component.scss'],
7+
})
8+
export class AppComponent {
9+
title = 'angular';
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { NgModule } from '@angular/core'
2+
import { BrowserModule } from '@angular/platform-browser'
3+
4+
import { AppComponent } from './app.component'
5+
6+
@NgModule({
7+
declarations: [
8+
AppComponent,
9+
],
10+
imports: [
11+
BrowserModule,
12+
],
13+
providers: [],
14+
bootstrap: [AppComponent],
15+
})
16+
export class AppModule { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Component, EventEmitter, Output } from "@angular/core";
2+
3+
@Component({
4+
selector: 'app-button-output',
5+
template: `<button (click)="clicked.emit(true)">Click Me</button>`
6+
})
7+
export class ButtonOutputComponent {
8+
@Output() clicked: EventEmitter<boolean> = new EventEmitter()
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Component, Input } from "@angular/core";
2+
3+
@Component({
4+
selector: "child-component",
5+
template: "<h1>{{msg}}</h1>",
6+
})
7+
export class ChildComponent {
8+
@Input() msg!: string;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Component } from "@angular/core";
2+
import { CounterService } from "./counter.service";
3+
4+
@Component({
5+
selector: "counter-component",
6+
template: `<button (click)="increment()">
7+
Increment: {{ count$ | async }}
8+
</button>`,
9+
})
10+
export class CounterComponent {
11+
count$ = this.counterService.count$;
12+
13+
constructor(private counterService: CounterService) {}
14+
15+
increment() {
16+
this.counterService.increment();
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Injectable } from "@angular/core";
2+
import { BehaviorSubject } from "rxjs";
3+
4+
@Injectable()
5+
export class CounterService {
6+
private count = new BehaviorSubject<number>(0);
7+
public count$ = this.count.asObservable();
8+
9+
public increment() {
10+
this.count.next(this.count.value + 1);
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Component, Input } from "@angular/core";
2+
3+
@Component({
4+
selector: "errors-component",
5+
template: `<div>
6+
<button id="sync-error" (click)="syncError()">Sync Error</button>
7+
<button id="async-error" (click)="asyncError()">Sync Error</button>
8+
</div>`,
9+
})
10+
export class ErrorsComponent {
11+
@Input() throwError!: boolean;
12+
13+
syncError() {
14+
throw new Error('sync error')
15+
}
16+
17+
asyncError() {
18+
setTimeout(() => {
19+
throw new Error('async error')
20+
}, 50)
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { NgModule } from '@angular/core'
2+
import { ParentComponent } from './parent.component'
3+
import { ChildComponent } from './child.component'
4+
5+
@NgModule({
6+
declarations: [ParentComponent, ChildComponent],
7+
}) export class ParentChildModule {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Component } from "@angular/core";
2+
3+
@Component({
4+
selector: "parent-component",
5+
template: '<child-component [msg]="msg"></child-component>',
6+
})
7+
export class ParentComponent {
8+
msg = "Hello World from ParentComponent";
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Component } from '@angular/core'
2+
3+
@Component({
4+
selector: 'app-projection',
5+
template: `<h3><ng-content></ng-content></h3>`
6+
})
7+
export class ProjectionComponent {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Component } from "@angular/core";
2+
3+
@Component({
4+
selector: "with-directives-component",
5+
template: ` <button (click)="show = !show">Toggle Message</button>
6+
<ul *ngIf="show">
7+
<li *ngFor="let item of items">{{ item }}</li>
8+
</ul>`,
9+
})
10+
export class WithDirectivesComponent {
11+
show = true;
12+
13+
items = ["breakfast", "lunch", "dinner"];
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ErrorsComponent } from './components/errors'
2+
3+
describe('Errors', () => {
4+
5+
it('error on mount', () => {
6+
cy.mount(ErrorsComponent, { componentProperties: { throwError: true } })
7+
})
8+
9+
it('sync error', () => {
10+
cy.mount(ErrorsComponent)
11+
cy.get('#sync-error').click()
12+
})
13+
14+
it('async error', () => {
15+
cy.mount(ErrorsComponent)
16+
cy.get('#async-error').click()
17+
})
18+
19+
it('command failure', { defaultCommandTimeout: 50 }, () => {
20+
cy.mount(ErrorsComponent)
21+
cy.get('element-that-does-not-exist')
22+
})
23+
})

0 commit comments

Comments
 (0)