├── Angular17 ├── src │ ├── assets │ │ └── .gitkeep │ ├── app │ │ ├── app.component.css │ │ ├── app.routes.ts │ │ ├── app.config.ts │ │ ├── app.component.ts │ │ ├── app.component.spec.ts │ │ └── app.component.html │ ├── styles.css │ ├── favicon.ico │ ├── main.ts │ └── index.html ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json ├── README.md ├── tsconfig.app.json ├── tsconfig.spec.json ├── .editorconfig ├── .gitignore ├── package.json ├── tsconfig.json └── angular.json ├── Angular 15 and below ├── src │ ├── assets │ │ └── .gitkeep │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── continent-service │ │ │ ├── continent.ts │ │ │ ├── country.ts │ │ │ ├── continent.service.class.spec.ts │ │ │ ├── continent.service.spec.ts │ │ │ ├── continent.service.ng-mocks.spec.ts │ │ │ └── continent.service.ts │ │ ├── app.component.ts │ │ ├── edit-country │ │ │ ├── edit-country.component.scss │ │ │ ├── activated-route-mock.ts │ │ │ ├── edit-country.module.ts │ │ │ ├── save-country.spec.ts │ │ │ ├── save-country-with-dummy-component.spec.ts │ │ │ ├── edit-country.component.html │ │ │ ├── edit-country.component.class.spec.ts │ │ │ ├── edit-country.component.ts │ │ │ ├── __snapshots__ │ │ │ │ ├── save-country-with-dummy-component.spec.ts.snap │ │ │ │ └── save-country.spec.ts.snap │ │ │ └── edit-country.component.component.spec.ts │ │ ├── app-routing.module.ts │ │ ├── app.module.ts │ │ ├── mocking │ │ │ └── simple-functions.spec.ts │ │ └── app.component.spec.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── styles.css │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── polyfills.ts ├── setup-jest.ts ├── tsconfig-esm.spec.json ├── .editorconfig ├── tsconfig.spec.json ├── jest.config.js ├── tsconfig.app.json ├── jest-esm.config.js ├── jest-global-mocks.ts ├── tsconfig.json ├── .browserslistrc ├── .gitignore ├── package.json ├── README.md ├── angular.json └── LICENSE ├── angular17-workspace ├── projects │ ├── app1 │ │ ├── src │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ ├── app │ │ │ │ ├── app.component.css │ │ │ │ ├── app.component.html │ │ │ │ ├── app.routes.ts │ │ │ │ ├── app.config.ts │ │ │ │ ├── app.component.ts │ │ │ │ └── app.component.spec.ts │ │ │ ├── styles.css │ │ │ ├── favicon.ico │ │ │ ├── main.ts │ │ │ └── index.html │ │ ├── setup-jest.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── jest.config.js │ ├── app2 │ │ ├── src │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ ├── app │ │ │ │ ├── app.component.css │ │ │ │ ├── app.routes.ts │ │ │ │ ├── app.config.ts │ │ │ │ ├── app.component.ts │ │ │ │ ├── app.component.spec.ts │ │ │ │ └── app.component.html │ │ │ ├── styles.css │ │ │ ├── favicon.ico │ │ │ ├── main.ts │ │ │ └── index.html │ │ ├── setup-jest.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── jest.config.js │ └── shared-library │ │ ├── setup-jest.ts │ │ ├── src │ │ ├── public-api.ts │ │ └── lib │ │ │ ├── shared-library.component.ts │ │ │ ├── shared-library.service.ts │ │ │ ├── shared-library.service.spec.ts │ │ │ └── shared-library.component.spec.ts │ │ ├── ng-package.json │ │ ├── package.json │ │ ├── tsconfig.lib.prod.json │ │ ├── tsconfig.spec.json │ │ ├── tsconfig.lib.json │ │ ├── jest.config.js │ │ └── README.md ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json ├── .editorconfig ├── .gitignore ├── README.md ├── tsconfig.json ├── package.json └── angular.json ├── angular17-JustJeb-workspace ├── projects │ ├── app1 │ │ ├── src │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ ├── app │ │ │ │ ├── app.component.css │ │ │ │ ├── app.component.html │ │ │ │ ├── app.routes.ts │ │ │ │ ├── app.config.ts │ │ │ │ ├── app.component.ts │ │ │ │ └── app.component.spec.ts │ │ │ ├── styles.css │ │ │ ├── favicon.ico │ │ │ ├── main.ts │ │ │ └── index.html │ │ ├── setup-jest.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── jest.config.js │ ├── app2 │ │ ├── src │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ ├── app │ │ │ │ ├── app.component.css │ │ │ │ ├── app.routes.ts │ │ │ │ ├── app.config.ts │ │ │ │ ├── app.component.ts │ │ │ │ ├── app.component.spec.ts │ │ │ │ └── app.component.html │ │ │ ├── styles.css │ │ │ ├── favicon.ico │ │ │ ├── main.ts │ │ │ └── index.html │ │ ├── setup-jest.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── jest.config.js │ └── shared-library │ │ ├── setup-jest.ts │ │ ├── src │ │ ├── public-api.ts │ │ └── lib │ │ │ ├── shared-library.component.ts │ │ │ ├── shared-library.service.ts │ │ │ ├── shared-library.service.spec.ts │ │ │ └── shared-library.component.spec.ts │ │ ├── ng-package.json │ │ ├── package.json │ │ ├── tsconfig.lib.prod.json │ │ ├── tsconfig.spec.json │ │ ├── tsconfig.lib.json │ │ ├── jest.config.js │ │ └── README.md ├── setup-jest.ts ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json ├── .editorconfig ├── tsconfig.spec.json ├── .gitignore ├── tsconfig.json ├── package.json ├── README.md └── angular.json ├── .DS_Store ├── .vscode └── settings.json └── README.md /Angular17/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Angular17/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Angular 15 and below/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/.DS_Store -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /Angular17/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Angular 15 and below/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | import './jest-global-mocks'; 3 | -------------------------------------------------------------------------------- /Angular17/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/Angular17/src/favicon.ico -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /Angular 15 and below/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /Angular 15 and below/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Angular17/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = []; 4 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

App 1

2 |

{{sharedLibraryService.helloWorld()}}

3 | -------------------------------------------------------------------------------- /Angular 15 and below/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/Angular 15 and below/src/favicon.ico -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

App 1

2 |

{{sharedLibraryService.helloWorld()}}

3 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
Jest testing example is running!
2 | 3 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = []; 4 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = []; 4 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = []; 4 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = []; 4 | -------------------------------------------------------------------------------- /Angular 15 and below/tsconfig-esm.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.spec.json", 3 | "compilerOptions": { 4 | "module": "ESNext" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/angular17-workspace/projects/app1/src/favicon.ico -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/angular17-workspace/projects/app2/src/favicon.ico -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/angular17-JustJeb-workspace/projects/app1/src/favicon.ico -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephanrauh/angular-jest/HEAD/angular17-JustJeb-workspace/projects/app2/src/favicon.ico -------------------------------------------------------------------------------- /Angular17/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /angular17-workspace/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/continent-service/continent.ts: -------------------------------------------------------------------------------- 1 | import { Country } from './country'; 2 | 3 | export interface Continent { 4 | name: string; 5 | countriesInContinent: Array; 6 | } 7 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/continent-service/country.ts: -------------------------------------------------------------------------------- 1 | export interface Country { 2 | name: string, 3 | region: string, // this is the continent! 4 | population: number, 5 | flag: string 6 | } 7 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of shared-library 3 | */ 4 | 5 | export * from './lib/shared-library.service'; 6 | export * from './lib/shared-library.component'; 7 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of shared-library 3 | */ 4 | 5 | export * from './lib/shared-library.service'; 6 | export * from './lib/shared-library.component'; 7 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/shared-library", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/shared-library", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /Angular17/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | 6 | export const appConfig: ApplicationConfig = { 7 | providers: [provideRouter(routes)] 8 | }; 9 | -------------------------------------------------------------------------------- /Angular17/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /Angular17/README.md: -------------------------------------------------------------------------------- 1 | # Testing Angular 17 with the experimental built-in Jest support 2 | 3 | This project is more or less the standard project created with `ng new`. The only difference is I activated 4 | the experimental Jest support and adapted the `app.component.spec` to prove Jest is used. 5 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts" 9 | ], 10 | "include": [ 11 | "src/**/*.d.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'], 7 | }) 8 | export class AppComponent { 9 | title = 'example-app-v12'; 10 | } 11 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | 6 | export const appConfig: ApplicationConfig = { 7 | providers: [provideRouter(routes)] 8 | }; 9 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | 6 | export const appConfig: ApplicationConfig = { 7 | providers: [provideRouter(routes)] 8 | }; 9 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | 6 | export const appConfig: ApplicationConfig = { 7 | providers: [provideRouter(routes)] 8 | }; 9 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts" 9 | ], 10 | "include": [ 11 | "src/**/*.d.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | 6 | export const appConfig: ApplicationConfig = { 7 | providers: [provideRouter(routes)] 8 | }; 9 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/edit-country.component.scss: -------------------------------------------------------------------------------- 1 | 2 | .padding { 3 | padding-left: 8px; 4 | padding-right: 8px; 5 | padding-bottom: 20px; 6 | padding-top: 20px; 7 | } 8 | 9 | input { 10 | min-width: 30rem; 11 | } 12 | 13 | .error { 14 | color: red; 15 | margin-top: -13px; 16 | } 17 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared-library", 3 | "version": "0.0.1", 4 | "peerDependencies": { 5 | "@angular/common": "^17.0.0", 6 | "@angular/core": "^17.0.0" 7 | }, 8 | "dependencies": { 9 | "tslib": "^2.3.0" 10 | }, 11 | "sideEffects": false 12 | } 13 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared-library", 3 | "version": "0.0.1", 4 | "peerDependencies": { 5 | "@angular/common": "^17.0.0", 6 | "@angular/core": "^17.0.0" 7 | }, 8 | "dependencies": { 9 | "tslib": "^2.3.0" 10 | }, 11 | "sideEffects": false 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "jest.autoRun": { 3 | "watch": false, 4 | "onSave": "test-src-file", 5 | "onStartup": [ 6 | "all-tests" 7 | ] 8 | }, 9 | "jest.debugCodeLens.showWhenTestStateIn": [ 10 | "pass", 11 | "fail", 12 | "unknown" 13 | ], 14 | "jest.coverageFormatter": "GutterFormatter", 15 | } 16 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Angular17/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Angular17/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Angular17/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /Angular17/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular17 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /angular17-workspace/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /Angular 15 and below/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ExampleAppV11 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ] 9 | }, 10 | "include": [ 11 | "**/*.spec.ts", 12 | "**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/src/lib/shared-library.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'lib-shared-library', 5 | standalone: true, 6 | imports: [], 7 | template: ` 8 |

9 | shared-library works! 10 |

11 | `, 12 | styles: `` 13 | }) 14 | export class SharedLibraryComponent { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Angular 15 and below/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | max_line_length=140 14 | 15 | [*.md] 16 | max_line_length = off 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /Angular 15 and below/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "esModuleInterop": true, 6 | "outDir": "./out-tsc/spec", 7 | "types": [ 8 | "jest" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*.spec.ts", 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/src/lib/shared-library.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'lib-shared-library', 5 | standalone: true, 6 | imports: [], 7 | template: ` 8 |

9 | shared-library works! 10 |

11 | `, 12 | styles: `` 13 | }) 14 | export class SharedLibraryComponent { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "esModuleInterop": true, 6 | "outDir": "./out-tsc/spec", 7 | "types": [ 8 | "jest" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*.spec.ts", 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Angular 15 and below/jest.config.js: -------------------------------------------------------------------------------- 1 | // require('jest-preset-angular/ngcc-jest-processor'); 2 | 3 | globalThis.ngJest = { 4 | skipNgcc: true, 5 | tsconfig: 'tsconfig.spec.json', // this is the project root tsconfig 6 | }; 7 | 8 | /** @type {import('@jest/types').Config.InitialOptions} */ 9 | module.exports = { 10 | preset: 'jest-preset-angular', 11 | setupFilesAfterEnv: ['/setup-jest.ts'], 12 | }; 13 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [] 10 | }, 11 | "exclude": [ 12 | "**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ], 9 | "emitDecoratorMetadata": true, 10 | }, 11 | "include": [ 12 | "src/**/*.spec.ts", 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ], 9 | "emitDecoratorMetadata": true 10 | }, 11 | "include": [ 12 | "src/**/*.spec.ts", 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jest" 8 | ], 9 | "emitDecoratorMetadata": true 10 | }, 11 | "include": [ 12 | "**/*.spec.ts", 13 | "**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [] 10 | }, 11 | "exclude": [ 12 | "**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Angular 15 and below/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/src/lib/shared-library.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class SharedLibraryService { 7 | 8 | private counter = 1; 9 | 10 | constructor() { } 11 | 12 | public incrementCounter(): void { 13 | this.counter++; 14 | } 15 | 16 | public helloWorld(): string { 17 | return `¡Hola mundo ${this.counter}!`; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/src/lib/shared-library.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class SharedLibraryService { 7 | 8 | private counter = 1; 9 | 10 | constructor() { } 11 | 12 | public incrementCounter(): void { 13 | this.counter++; 14 | } 15 | 16 | public helloWorld(): string { 17 | return `¡Hola mundo ${this.counter}!`; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Angular 15 and below/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ], 15 | "exclude": [ 16 | "jest-global.mocks.ts", 17 | "setup-jest.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterOutlet } from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | standalone: true, 8 | imports: [CommonModule, RouterOutlet], 9 | templateUrl: './app.component.html', 10 | styleUrl: './app.component.css' 11 | }) 12 | export class AppComponent { 13 | title = 'app2'; 14 | } 15 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterOutlet } from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | standalone: true, 8 | imports: [CommonModule, RouterOutlet], 9 | templateUrl: './app.component.html', 10 | styleUrl: './app.component.css' 11 | }) 12 | export class AppComponent { 13 | title = 'app2'; 14 | } 15 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes: Routes = [ 5 | {path: 'country', loadChildren: () => import('./edit-country/edit-country.module').then(m => m.EditCountryModule)}, 6 | {path: '**', redirectTo: "country/Spain"} 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forRoot(routes)], 11 | exports: [RouterModule], 12 | }) 13 | export class AppRoutingModule {} 14 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-preset-angular', 3 | setupFilesAfterEnv: ['/setup-jest.ts'], 4 | testPathIgnorePatterns: [ 5 | '/node_modules/', 6 | '/dist/', 7 | '/src/test.ts', 8 | ], 9 | globals: { 10 | 'ts-jest': { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.html$', 13 | }, 14 | }, 15 | testPathIgnorePatterns: [ 16 | 17 | ] 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /Angular17/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterOutlet } from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | standalone: true, 8 | imports: [CommonModule, RouterOutlet], 9 | templateUrl: './app.component.html', 10 | styleUrl: './app.component.css' 11 | }) 12 | export class AppComponent { 13 | title = 'Angular 17'; 14 | 15 | public greet(): string { 16 | return 'Hello, Angular 17'; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/src/lib/shared-library.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SharedLibraryService } from './shared-library.service'; 4 | 5 | describe('SharedLibraryService', () => { 6 | let service: SharedLibraryService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(SharedLibraryService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/src/lib/shared-library.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SharedLibraryService } from './shared-library.service'; 4 | 5 | describe('SharedLibraryService', () => { 6 | let service: SharedLibraryService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(SharedLibraryService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/jest.config.js: -------------------------------------------------------------------------------- 1 | // This file is required by the IDE plugin. If you only need `ng test` to work, you can delete it. 2 | // The JustJeb plugin only needs the configuration in angular.json. 3 | 4 | globalThis.ngJest = { 5 | skipNgcc: true, 6 | tsconfig: 'tsconfig.spec.json', // this is the project root tsconfig 7 | }; 8 | 9 | /** @type {import('@jest/types').Config.InitialOptions} */ 10 | module.exports = { 11 | preset: 'jest-preset-angular', 12 | setupFilesAfterEnv: ['/setup-jest.ts'], 13 | }; 14 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/jest.config.js: -------------------------------------------------------------------------------- 1 | // This file is required by the IDE plugin. If you only need `ng test` to work, you can delete it. 2 | // The JustJeb plugin only needs the configuration in angular.json. 3 | 4 | globalThis.ngJest = { 5 | skipNgcc: true, 6 | tsconfig: 'tsconfig.spec.json', // this is the project root tsconfig 7 | }; 8 | 9 | /** @type {import('@jest/types').Config.InitialOptions} */ 10 | module.exports = { 11 | preset: 'jest-preset-angular', 12 | setupFilesAfterEnv: ['/setup-jest.ts'], 13 | }; 14 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/jest.config.js: -------------------------------------------------------------------------------- 1 | // This file is required by the IDE plugin. If you only need `ng test` to work, you can delete it. 2 | // The JustJeb plugin only needs the configuration in angular.json. 3 | 4 | globalThis.ngJest = { 5 | skipNgcc: true, 6 | tsconfig: 'tsconfig.spec.json', // this is the project root tsconfig 7 | }; 8 | 9 | /** @type {import('@jest/types').Config.InitialOptions} */ 10 | module.exports = { 11 | preset: 'jest-preset-angular', 12 | setupFilesAfterEnv: ['/setup-jest.ts'], 13 | }; 14 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-preset-angular', 3 | setupFilesAfterEnv: ['/setup-jest.ts'], 4 | testPathIgnorePatterns: [ 5 | '/node_modules/', 6 | '/dist/', 7 | '/src/test.ts', 8 | ], 9 | globals: { 10 | 'ts-jest': { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.html$', 13 | }, 14 | }, 15 | moduleNameMapper: { 16 | // If you have paths in tsconfig.app.json, add them here 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-preset-angular', 3 | setupFilesAfterEnv: ['/setup-jest.ts'], 4 | testPathIgnorePatterns: [ 5 | '/node_modules/', 6 | '/dist/', 7 | '/src/test.ts', 8 | ], 9 | globals: { 10 | 'ts-jest': { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.html$', 13 | }, 14 | }, 15 | moduleNameMapper: { 16 | // If you have paths in tsconfig.app.json, add them here 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /Angular 15 and below/jest-esm.config.js: -------------------------------------------------------------------------------- 1 | require('jest-preset-angular/ngcc-jest-processor'); 2 | const snapshotSerializers = require('jest-preset-angular/build/serializers/index'); 3 | 4 | /** @type {import('@jest/types').Config.InitialOptions} */ 5 | module.exports = { 6 | preset: 'jest-preset-angular/presets/defaults-esm', 7 | globals: { 8 | 'ts-jest': { 9 | tsconfig: '/tsconfig-esm.spec.json', 10 | stringifyContentPathRegex: '\\.html$', 11 | }, 12 | }, 13 | snapshotSerializers, 14 | setupFilesAfterEnv: ['/setup-jest.ts'], 15 | }; 16 | -------------------------------------------------------------------------------- /Angular17/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /angular17-workspace/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { HttpClientModule } from '@angular/common/http'; 7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 8 | 9 | 10 | @NgModule({ 11 | declarations: [AppComponent], 12 | imports: [BrowserModule, AppRoutingModule, HttpClientModule, BrowserAnimationsModule], 13 | providers: [], 14 | bootstrap: [AppComponent], 15 | }) 16 | export class AppModule {} 17 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterOutlet } from '@angular/router'; 4 | import { SharedLibraryService } from 'shared-library'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | standalone: true, 9 | imports: [CommonModule, RouterOutlet], 10 | templateUrl: './app.component.html', 11 | styleUrl: './app.component.css' 12 | }) 13 | export class AppComponent { 14 | title = 'app1'; 15 | 16 | constructor(public sharedLibraryService: SharedLibraryService) { 17 | this.sharedLibraryService.incrementCounter(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/mocking/simple-functions.spec.ts: -------------------------------------------------------------------------------- 1 | it('should add 42', () => { 2 | // given 3 | const mockCalculation = jest.fn((x) => 42 + x); 4 | 5 | // when 6 | const result = mockCalculation(21); 7 | const result2 = mockCalculation(22); 8 | 9 | // then 10 | expect(mockCalculation).toHaveBeenCalledWith(22); 11 | expect(mockCalculation).toHaveBeenCalledWith(21); 12 | expect(mockCalculation).toHaveBeenNthCalledWith(1, 21); 13 | expect(mockCalculation).lastCalledWith(22); 14 | expect(mockCalculation).toHaveBeenCalled(); 15 | expect(mockCalculation).toHaveBeenCalledTimes(2); 16 | expect(mockCalculation).toHaveReturnedWith(64); 17 | }); 18 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterOutlet } from '@angular/router'; 4 | import { SharedLibraryService } from 'shared-library'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | standalone: true, 9 | imports: [CommonModule, RouterOutlet], 10 | templateUrl: './app.component.html', 11 | styleUrl: './app.component.css' 12 | }) 13 | export class AppComponent { 14 | title = 'app1'; 15 | 16 | constructor(public sharedLibraryService: SharedLibraryService) { 17 | this.sharedLibraryService.incrementCounter(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Angular 15 and below/jest-global-mocks.ts: -------------------------------------------------------------------------------- 1 | Object.defineProperty(window, 'CSS', { value: null }); 2 | Object.defineProperty(document, 'doctype', { 3 | value: '', 4 | }); 5 | Object.defineProperty(window, 'getComputedStyle', { 6 | value: () => { 7 | return { 8 | display: 'none', 9 | appearance: ['-webkit-appearance'], 10 | }; 11 | }, 12 | }); 13 | /** 14 | * ISSUE: https://github.com/angular/material2/issues/7101 15 | * Workaround for JSDOM missing transform property 16 | */ 17 | Object.defineProperty(document.body.style, 'transform', { 18 | value: () => { 19 | return { 20 | enumerable: true, 21 | configurable: true, 22 | }; 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/activated-route-mock.ts: -------------------------------------------------------------------------------- 1 | import { ActivatedRoute } from '@angular/router'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | export const mockActivateRouteValue = { 5 | data: new BehaviorSubject({data: 'some additional data'}), 6 | params: new BehaviorSubject({country: 'Spain'}), 7 | snapshot: { 8 | url: [ 9 | { 10 | path: 'country', 11 | }, 12 | { 13 | path: 'Spain', 14 | }, 15 | ], 16 | } 17 | } as unknown as ActivatedRoute; // fancy typecast allows us to omit unused values 18 | 19 | export const mockActivatedRoute = { 20 | provide: ActivatedRoute, 21 | useValue: mockActivateRouteValue 22 | }; 23 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/continent-service/continent.service.class.spec.ts: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { ContinentService } from './continent.service'; 3 | 4 | const countries = [ 5 | { 6 | name: 'España', 7 | region: 'Europa', 8 | population: 47000000, 9 | flag: 'https://flags.com/spain', 10 | }, 11 | ]; 12 | 13 | jest.mock('./continent.service'); 14 | ContinentService.prototype.countries$ = of(countries); 15 | 16 | const service = new ContinentService(null); 17 | 18 | describe('ContinentService', () => { 19 | it('should be created', async () => { 20 | const result = await service.countries$.toPromise(); 21 | expect(result[0].name).toBe('España'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /Angular 15 and below/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "sourceMap": true, 9 | "declaration": false, 10 | "downlevelIteration": true, 11 | "experimentalDecorators": true, 12 | "moduleResolution": "node", 13 | "importHelpers": true, 14 | "target": "es2015", 15 | "module": "es2020", 16 | "lib": [ 17 | "es2018", 18 | "dom" 19 | ], 20 | }, 21 | "angularCompilerOptions": { 22 | "strict": true, 23 | "strictTemplates": true 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Angular17/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /angular17-workspace/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /Angular 15 and below/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /Angular 15 and below/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 18 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/src/lib/shared-library.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SharedLibraryComponent } from './shared-library.component'; 4 | 5 | describe('SharedLibraryComponent', () => { 6 | let component: SharedLibraryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [SharedLibraryComponent] 12 | }) 13 | .compileComponents(); 14 | 15 | fixture = TestBed.createComponent(SharedLibraryComponent); 16 | component = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(component).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/src/lib/shared-library.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SharedLibraryComponent } from './shared-library.component'; 4 | 5 | describe('SharedLibraryComponent', () => { 6 | let component: SharedLibraryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [SharedLibraryComponent] 12 | }) 13 | .compileComponents(); 14 | 15 | fixture = TestBed.createComponent(SharedLibraryComponent); 16 | component = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(component).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /Angular 15 and below/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | .angular 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # profiling files 15 | chrome-profiler-events*.json 16 | speed-measure-plugin*.json 17 | 18 | # IDEs and editors 19 | /.idea 20 | .project 21 | .classpath 22 | .c9/ 23 | *.launch 24 | .settings/ 25 | *.sublime-workspace 26 | 27 | # IDE - VSCode 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | .history/* 34 | 35 | # misc 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/edit-country.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { EditCountryComponent } from './edit-country.component'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | import { ConfirmDialogModule } from 'primeng/confirmdialog'; 7 | import { ConfirmationService } from 'primeng/api'; 8 | import { InputTextModule } from 'primeng/inputtext'; 9 | import { ButtonModule } from 'primeng/button'; 10 | 11 | const routes: Routes = [{ path: ':country', component: EditCountryComponent }]; 12 | 13 | @NgModule({ 14 | declarations: [EditCountryComponent], 15 | providers: [ConfirmationService], 16 | imports: [CommonModule, RouterModule.forChild(routes), ReactiveFormsModule, ConfirmDialogModule, InputTextModule, ButtonModule], 17 | }) 18 | export class EditCountryModule {} 19 | -------------------------------------------------------------------------------- /angular17-workspace/README.md: -------------------------------------------------------------------------------- 1 | # Multi-project with two apps sharing a common library 2 | 3 | This project configures Jest manually, without using a builder like Just Jeb. The goal is to support both the IDE integration and the command line. 4 | 5 | ## npm link 6 | 7 | `cd dist/shared-library && npm link` creates a symbolic link in the global `node_modules` folder. This points to the dist folder. If you delete the dist folder, the symbolic link is broken. 8 | 9 | If you call `npm link shared-library` before building the app, the `node_modules` folder of your app gets a symbolic link to the library. This way you can use the library in your app without having to publish it to npm. 10 | 11 | ## Key to success 12 | Probably the key to success is adding `"preserveSymlinks": true,` to the tsconfig.json files. 13 | 14 | It also turned out to be useful to configure Jest in each subproject individually. There's no global Jest configuration in this project. The only common file is the global `tsconfig.json`. 15 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have the 'app2' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('app2'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('Hello, app2'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have the 'app2' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('app2'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('Hello, app2'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /Angular17/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular17", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^17.0.0", 14 | "@angular/common": "^17.0.0", 15 | "@angular/compiler": "^17.0.0", 16 | "@angular/core": "^17.0.0", 17 | "@angular/forms": "^17.0.0", 18 | "@angular/platform-browser": "^17.0.0", 19 | "@angular/platform-browser-dynamic": "^17.0.0", 20 | "@angular/router": "^17.0.0", 21 | "rxjs": "~7.8.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.14.2" 24 | }, 25 | "devDependencies": { 26 | "@angular-builders/jest": "^17.0.0", 27 | "@angular-devkit/build-angular": "^17.0.7", 28 | "@angular/cli": "^17.0.7", 29 | "@angular/compiler-cli": "^17.0.0", 30 | "@types/jasmine": "~5.1.0", 31 | "@types/jest": "^29.5.11", 32 | "jest": "^29.7.0", 33 | "typescript": "~5.2.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Testing an Angular application with Jest 2 | 3 | ## Comments appreciated 4 | 5 | I've created this repository to learn how to use Jest and to collect best practices. At the time of writing, I'm a rookie, and you - dear reader - are probably more expert than me. In other words: I'm open to suggestions on how to improve the demos and the corresponding article on https://www.beyondjava.net/jest-mocks-and-spies. 6 | 7 | ## Angular 17 8 | 9 | The Angular team published experimental support for Jest with Angular 16, but it took me until Angular 17 to take it for a ride. You find the result in the `Angular 17` folder. It's probably not perfect: the Angular team hasn't published a tutorial yet (or I didn't find it), so much of my code is guesswork. 10 | 11 | ## Angular 15 and below 12 | 13 | Before Angular 16, there was no official support for Jest, but several great community solutions. The folder `Angular 15 and below` shows how to use Jest with an older Angular version. When I created the repository, Angular was at version 12, so I guess the recipe works for all versions from 12 to 15. 14 | 15 | -------------------------------------------------------------------------------- /Angular17/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "declaration": false, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": [ 23 | "ES2022", 24 | "dom" 25 | ], 26 | "types": [ 27 | "jest" 28 | ] 29 | }, 30 | "angularCompilerOptions": { 31 | "enableI18nLegacyMessageIdFormat": false, 32 | "strictInjectionParameters": true, 33 | "strictInputAccessModifiers": true, 34 | "strictTemplates": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /angular17-workspace/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "declaration": false, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "preserveSymlinks": true, 19 | "importHelpers": true, 20 | "target": "ES2022", 21 | "module": "ES2022", 22 | "useDefineForClassFields": false, 23 | "lib": [ 24 | "ES2022", 25 | "dom" 26 | ] 27 | }, 28 | "angularCompilerOptions": { 29 | "enableI18nLegacyMessageIdFormat": false, 30 | "strictInjectionParameters": true, 31 | "strictInputAccessModifiers": true, 32 | "strictTemplates": true, 33 | "preserveSymlinks": true, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Angular17/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "declaration": false, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "preserveSymlinks": true, 19 | "importHelpers": true, 20 | "target": "ES2022", 21 | "module": "ES2022", 22 | "useDefineForClassFields": false, 23 | "lib": [ 24 | "ES2022", 25 | "dom" 26 | ] 27 | }, 28 | "angularCompilerOptions": { 29 | "enableI18nLegacyMessageIdFormat": false, 30 | "strictInjectionParameters": true, 31 | "strictInputAccessModifiers": true, 32 | "strictTemplates": true, 33 | "preserveSymlinks": true, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /angular17-workspace/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /angular17-workspace/projects/shared-library/README.md: -------------------------------------------------------------------------------- 1 | # SharedLibrary 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project shared-library` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project shared-library`. 8 | > Note: Don't forget to add `--project shared-library` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build shared-library` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build shared-library`, go to the dist folder `cd dist/shared-library` and run `npm publish`. 17 | 18 | ## Running unit tests 19 | 20 | Run `ng test shared-library` to execute the unit tests via [Karma](https://karma-runner.github.io). 21 | 22 | ## Further help 23 | 24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 25 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/shared-library/README.md: -------------------------------------------------------------------------------- 1 | # SharedLibrary 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project shared-library` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project shared-library`. 8 | > Note: Don't forget to add `--project shared-library` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build shared-library` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build shared-library`, go to the dist folder `cd dist/shared-library` and run `npm publish`. 17 | 18 | ## Running unit tests 19 | 20 | Run `ng test shared-library` to execute the unit tests via [Karma](https://karma-runner.github.io). 21 | 22 | ## Further help 23 | 24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 25 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(async () => { 8 | await TestBed.configureTestingModule({ 9 | imports: [RouterTestingModule], 10 | declarations: [AppComponent], 11 | }).compileComponents(); 12 | }); 13 | 14 | it('should create the app', () => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | const app = fixture.componentInstance; 17 | expect(app).toBeTruthy(); 18 | }); 19 | 20 | it(`should have as title 'example-app-v12'`, () => { 21 | const fixture = TestBed.createComponent(AppComponent); 22 | const app = fixture.componentInstance; 23 | expect(app.title).toEqual('example-app-v12'); 24 | }); 25 | 26 | it('should render title', () => { 27 | const fixture = TestBed.createComponent(AppComponent); 28 | fixture.detectChanges(); 29 | const compiled = fixture.nativeElement; 30 | expect(compiled.querySelector('.title').textContent).toContain('Jest testing example is running!'); 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/continent-service/continent.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ContinentService } from './continent.service'; 4 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; 5 | 6 | describe('ContinentService', () => { 7 | let service: ContinentService; 8 | 9 | beforeEach(() => { 10 | TestBed.configureTestingModule({ imports: [HttpClientTestingModule] }); 11 | service = TestBed.inject(ContinentService); 12 | }); 13 | 14 | it('should be created', async () => { 15 | expect(service).toBeTruthy(); 16 | const httpMock = TestBed.inject(HttpTestingController); 17 | 18 | const countries = [ 19 | { 20 | name: 'España', 21 | region: 'Europa', 22 | population: 47000000, 23 | flag: 'https://flags.com/spain', 24 | }, 25 | ]; 26 | 27 | // const result = await service.countries$.toPromise(); // doesn't work! 28 | service.countries$.subscribe((result) => { 29 | expect(result[0].name).toBe('España'); 30 | }); 31 | 32 | const mockRequest = httpMock.expectOne('https://restcountries.com/v2/all'); 33 | mockRequest.flush(countries); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /Angular 15 and below/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-app-v12", 3 | "version": "12.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "jest --coverage", 9 | "test-esm": "node --experimental-vm-modules node_modules/jest/bin/jest.js -c=jest-esm.config.js" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^15.0.0", 14 | "@angular/common": "^15.0.0", 15 | "@angular/compiler": "^15.0.0", 16 | "@angular/core": "^15.0.0", 17 | "@angular/forms": "^15.2.10", 18 | "@angular/platform-browser": "^15.0.0", 19 | "@angular/platform-browser-dynamic": "^15.2.10", 20 | "@angular/router": "^15.2.10", 21 | "ng-mocks": "^14.12.1", 22 | "primeng": "^15.4.1", 23 | "rxjs": "~7.6.0", 24 | "tslib": "^2.6.2", 25 | "zone.js": "^0.12.0" 26 | }, 27 | "devDependencies": { 28 | "@angular-devkit/build-angular": "^15.2.10", 29 | "@angular/cli": "^15.2.10", 30 | "@angular/compiler-cli": "^15.2.10", 31 | "@types/jest": "^29.5.11", 32 | "@types/node": "^18.19.3", 33 | "jest": "^28.1.3", 34 | "jest-preset-angular": "^12.2.6", 35 | "ts-node": "~10.9.0", 36 | "typescript": "<4.9.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app1/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have the 'app1' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('app1'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('App 1'); 28 | }); 29 | 30 | it('should use the library', () => { 31 | const fixture = TestBed.createComponent(AppComponent); 32 | fixture.detectChanges(); 33 | const compiled = fixture.nativeElement as HTMLElement; 34 | expect(compiled.querySelector('p')?.textContent).toContain('¡Hola mundo 2!'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app1/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have the 'app1' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('app1'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('App 1'); 28 | }); 29 | 30 | it('should use the library', () => { 31 | const fixture = TestBed.createComponent(AppComponent); 32 | fixture.detectChanges(); 33 | const compiled = fixture.nativeElement as HTMLElement; 34 | expect(compiled.querySelector('p')?.textContent).toContain('¡Hola mundo 2!'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/save-country.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientTestingModule } from "@angular/common/http/testing"; 2 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 3 | import { NoopAnimationsModule } from "@angular/platform-browser/animations"; 4 | import { RouterTestingModule } from "@angular/router/testing"; 5 | import { ConfirmationService } from "primeng/api"; 6 | import { ConfirmDialogModule } from "primeng/confirmdialog"; 7 | import { EditCountryComponent } from "./edit-country.component"; 8 | 9 | describe('Save-Country-Question', () => { 10 | let fixture: ComponentFixture; 11 | let service: ConfirmationService; 12 | let component: EditCountryComponent; 13 | 14 | beforeEach(async () => { 15 | await TestBed.configureTestingModule({ 16 | declarations: [EditCountryComponent], 17 | providers: [ConfirmationService], 18 | imports: [ConfirmDialogModule, RouterTestingModule, HttpClientTestingModule, NoopAnimationsModule], 19 | }).compileComponents(); 20 | 21 | service = TestBed.inject(ConfirmationService); 22 | 23 | fixture = TestBed.createComponent(EditCountryComponent); 24 | component = fixture.componentInstance; 25 | 26 | fixture.detectChanges(); // ngOnInit() 27 | }); 28 | 29 | it("Should render the confirmation dialog", () => { 30 | service.confirm({ 31 | key: 'confirm-save-country', 32 | }); 33 | fixture.detectChanges(); 34 | const msgSpan = document.querySelector(".p-confirm-dialog-message") as HTMLElement; 35 | expect(msgSpan.innerHTML).toBe("Do you want to save the country?"); 36 | expect(document.body).toMatchSnapshot(); 37 | }) 38 | }); 39 | -------------------------------------------------------------------------------- /Angular17/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | test('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | test(`should have the 'Angular 17' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('Angular 17'); 21 | }); 22 | 23 | test('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Angular 17'); 28 | }); 29 | 30 | test('should greet', () => { 31 | const fixture = TestBed.createComponent(AppComponent); 32 | fixture.detectChanges(); 33 | const greeting = fixture.componentInstance.greet(); 34 | expect(greeting).toBe('Hello, Angular 17'); 35 | }); 36 | 37 | test('should mock the greeting', () => { 38 | AppComponent.prototype.greet = jest.fn().mockReturnValue('Bonjour, Angular dix-sept'); 39 | const fixture = TestBed.createComponent(AppComponent); 40 | fixture.detectChanges(); 41 | const greeting = fixture.componentInstance.greet(); 42 | expect(greeting).toBe('Bonjour, Angular dix-sept'); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/save-country-with-dummy-component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientTestingModule } from "@angular/common/http/testing"; 2 | import { Component } from "@angular/core"; 3 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 4 | import { NoopAnimationsModule } from "@angular/platform-browser/animations"; 5 | import { RouterTestingModule } from "@angular/router/testing"; 6 | import { ConfirmationService } from "primeng/api"; 7 | import { ConfirmDialogModule } from "primeng/confirmdialog"; 8 | 9 | @Component({template: ` 12 | `}) 13 | class DummyComponent {} 14 | 15 | describe('Save-Country-Question', () => { 16 | let fixture: ComponentFixture; 17 | 18 | let component: DummyComponent; 19 | 20 | beforeEach(async () => { 21 | await TestBed.configureTestingModule({ 22 | declarations: [DummyComponent], 23 | providers: [ConfirmationService], 24 | imports: [ConfirmDialogModule, RouterTestingModule, HttpClientTestingModule, NoopAnimationsModule], 25 | }).compileComponents(); 26 | 27 | fixture = TestBed.createComponent(DummyComponent); 28 | component = fixture.componentInstance; 29 | 30 | fixture.detectChanges(); 31 | }); 32 | 33 | it("Should render the confirmation dialog", () => { 34 | const service = TestBed.inject(ConfirmationService);; 35 | service.confirm({ 36 | key: 'confirm-save-country', 37 | }); 38 | fixture.detectChanges(); 39 | const msgSpan = document.querySelector(".p-confirm-dialog-message") as HTMLElement; 40 | expect(msgSpan.innerHTML).toBe("Do you want to save the country?"); 41 | expect(document.body).toMatchSnapshot(); 42 | }) 43 | }); 44 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular17-workspace", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "setup": "npm install && ng build --configuration production shared-library && cd dist/shared-library && npm link && npm link shared-library && cd .. && npm run build:app1 && npm run build:app2", 6 | "ng": "ng", 7 | "start:app1": "ng serve app1 -o --port 4200", 8 | "start:app2": "ng serve app2 -o --port 4201", 9 | "test:app1": "ng test app1", 10 | "test:app2": "ng test app2", 11 | "test:lib": "ng test shared-library", 12 | "build:lib": "ng build --configuration production shared-library", 13 | "watch:lib": "ng build shared-library --configuration production --watch", 14 | "build:app1": "npm link shared-library && ng build --configuration production app1", 15 | "build:app2": "npm link shared-library && ng build --configuration production app2" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@angular/animations": "^17.0.0", 20 | "@angular/common": "^17.0.0", 21 | "@angular/compiler": "^17.0.0", 22 | "@angular/core": "^17.0.0", 23 | "@angular/forms": "^17.0.0", 24 | "@angular/platform-browser": "^17.0.0", 25 | "@angular/platform-browser-dynamic": "^17.0.0", 26 | "@angular/router": "^17.0.0", 27 | "rxjs": "~7.8.0", 28 | "tslib": "^2.3.0", 29 | "zone.js": "~0.14.2" 30 | }, 31 | "devDependencies": { 32 | "@angular-builders/jest": "^17.0.0", 33 | "@angular-devkit/build-angular": "^17.0.7", 34 | "@angular/cli": "^17.0.7", 35 | "@angular/compiler-cli": "^17.0.0", 36 | "@types/jasmine": "~5.1.0", 37 | "@types/jest": "^29.5.11", 38 | "jasmine-core": "~5.1.0", 39 | "jest": "^29.7.0", 40 | "ng-packagr": "^17.0.0", 41 | "typescript": "~5.2.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /angular17-workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular17-workspace", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "setup": "npm install && ng build --configuration production shared-library && cd dist/shared-library && npm link && npm link shared-library && cd .. && npm run build:app1 && npm run build:app2", 6 | "ng": "ng", 7 | "start:app1": "ng serve app1 -o --port 4200", 8 | "start:app2": "ng serve app2 -o --port 4201", 9 | "test:app1": "jest --config projects/app1/jest.config.js", 10 | "test:app2": "jest --config projects/app2/jest.config.js", 11 | "test:lib": "jest --config projects/shared-library/jest.config.js", 12 | "build:lib": "ng build --configuration production shared-library", 13 | "watch:lib": "ng build shared-library --configuration production --watch", 14 | "build:app1": "npm link shared-library && ng build --configuration production app1", 15 | "build:app2": "npm link shared-library && ng build --configuration production app2" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@angular/animations": "^17.0.0", 20 | "@angular/common": "^17.0.0", 21 | "@angular/compiler": "^17.0.0", 22 | "@angular/core": "^17.0.0", 23 | "@angular/forms": "^17.0.0", 24 | "@angular/platform-browser": "^17.0.0", 25 | "@angular/platform-browser-dynamic": "^17.0.0", 26 | "@angular/router": "^17.0.0", 27 | "rxjs": "~7.8.0", 28 | "tslib": "^2.3.0", 29 | "zone.js": "~0.14.2" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "^17.0.7", 33 | "@angular/cli": "^17.0.7", 34 | "@angular/compiler-cli": "^17.0.0", 35 | "@types/jasmine": "~5.1.0", 36 | "@types/jest": "^29.5.11", 37 | "jest": "^29.7.0", 38 | "jest-preset-angular": "^13.1.4", 39 | "ng-packagr": "^17.0.0", 40 | "typescript": "~5.2.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Angular 15 and below/README.md: -------------------------------------------------------------------------------- 1 | # Testing an Angular application with Jest 2 | 3 | ## Comments appreciated 4 | 5 | I've created this repository to learn how to use Jest and to collect best practices. At the time of writing, I'm a rookie, and you - dear reader - are probably more expert than me. In other words: I'm open to suggestions on how to improve the demo and the corresponding article on https://www.beyondjava.net/jest-mocks-and-spies. 6 | 7 | ## Setup 8 | 9 | There are excellent tutorials out there (think of https://timdeschryver.dev/blog/integrate-jest-into-an-angular-application-and-library), but for some reason, most of them broken at one point or another. 10 | Usually, they work for older Angular versions, but not for the current version of Angular and Jest. 11 | 12 | So take one of the official example projects as a starting point: https://github.com/thymikee/jest-preset-angular/tree/master/examples. 13 | 14 | ## Key takeaways 15 | 16 | - Jest mocks are what other frameworks call "spies." 17 | - If you want to "mock away" a dependency, consider using an empty class instead of trying to use something like `jest.fn()`. In other words, if all you need is a simple stub, using a Jest function is both overkill and unnecessarily lengthy. 18 | - Large parts of your components can be tested as simple classes. You don't always need an Angular `TestBed.` Whenever possible, write tests without `TestBed.` Simple classes need less infrastructure, so tests using simple classes are both faster and simpler. 19 | - `ng-mocks` is a helpful framework simplifying a lot of things. 20 | - https://github.com/testing-library/angular-testing-library also looks promising. I haven't made up my mind about it yet, but it's worth a look. 21 | 22 | ## State of the art 23 | This repository is still evolving. As mentioned above, I invite you to share your knowledge with me, so this repository and the blog article get better! 24 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/README.md: -------------------------------------------------------------------------------- 1 | # Multi-project with two apps sharing a common library 2 | 3 | This project configures using a builder like Just Jeb (https://www.justjeb.com/post/angular-cli-ng-test-with-jest). The goal is to support both the IDE integration and the command line. 4 | 5 | ## npm link 6 | 7 | `cd dist/shared-library && npm link` creates a symbolic link in the global `node_modules` folder. This points to the dist folder. If you delete the dist folder, the symbolic link is broken. 8 | 9 | If you call `npm link shared-library` before building the app, the `node_modules` folder of your app gets a symbolic link to the library. This way you can use the library in your app without having to publish it to npm. 10 | 11 | ## Keys to success 12 | - Probably the key to success is adding `"preserveSymlinks": true,` to the tsconfig.json files. 13 | - It's also important to configure the attribute `modulePathIgnorePatterns` to the `angular.json`. Otherwise, the shared library is found twice, and Jest fails to resolve it: 14 | 15 | ```json 16 | "test": { 17 | "builder": "@angular-builders/jest:run", 18 | "options": { 19 | ..., 20 | "modulePathIgnorePatterns": [ 21 | "/projects/shared-library/" 22 | ] 23 | `````` 24 | 25 | - The builder requires you to configure Jest in each subproject individually. 26 | 27 | There's no global Jest configuration in this project. The only common file is the global `tsconfig.json`. 28 | 29 | ## Adding IDE support 30 | 31 | I've created this project with Visual Studio Code and the Jest Runner plugin. If you're using the more popular Jest plugin by Orta, the configuration is slightly different. 32 | 33 | The plugin requires you to add 34 | - the `jest.config.js` to every sub-project 35 | - the `setup-jest.ts` to every sub-project 36 | - and another copy of the `setup-jest.ts` to the root project. 37 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/continent-service/continent.service.ng-mocks.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { MockService } from 'ng-mocks'; 3 | import { of } from 'rxjs'; 4 | import { ContinentService } from './continent.service'; 5 | 6 | const countries = [ 7 | { 8 | name: 'España', 9 | region: 'Europa', 10 | population: 47000000, 11 | flag: 'https://flags.com/spain', 12 | }, 13 | ]; 14 | 15 | const countries$ = of(countries); 16 | 17 | describe('ContinentService', () => { 18 | let httpClientMock; 19 | let service; 20 | 21 | beforeEach(() => { 22 | httpClientMock = MockService(HttpClient); 23 | httpClientMock.get = jest.fn(() => countries$) as any; 24 | service = new ContinentService(httpClientMock); 25 | }); 26 | 27 | afterEach(() => { 28 | document.body.innerHTML = ''; 29 | document.head.innerHTML = ''; 30 | }); 31 | 32 | it('should be return the list of countries', async () => { 33 | const result = await service.countries$.toPromise(); 34 | 35 | expect(httpClientMock.get).toHaveBeenCalled(); 36 | expect(httpClientMock.get).toHaveLastReturnedWith(countries$); 37 | expect(httpClientMock.get).toHaveBeenCalledWith('https://restcountries.com/v2/all'); 38 | expect(result[0].name).toBe('España'); 39 | }); 40 | 41 | it('should use a fresh copy (part 1)', async () => { 42 | await service.countries$.toPromise(); 43 | service.used = true; 44 | httpClientMock.used = true; 45 | document.body.appendChild(document.createElement('canvas')); 46 | 47 | expect(httpClientMock.used).toBe(true); 48 | expect(document.body.querySelector('canvas')).not.toBeNull(); 49 | }); 50 | 51 | it('should use a fresh copy (part 2)', async () => { 52 | expect(service.used).not.toBe(true); 53 | expect(httpClientMock.get).toHaveBeenCalledTimes(1); 54 | expect(httpClientMock.used).not.toBe(true); 55 | expect(document.body.querySelector('canvas')).toBeNull(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/continent-service/continent.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Country } from './country'; 4 | import { map, tap } from 'rxjs/operators'; 5 | import { Continent } from './continent'; 6 | import { Observable } from 'rxjs'; 7 | 8 | @Injectable({ 9 | providedIn: 'root', 10 | }) 11 | export class ContinentService { 12 | 13 | public listOfContinents$!: Observable>; 14 | 15 | public countries$!: Observable; 16 | 17 | public continents$!: Observable>; 18 | 19 | constructor(private httpClient: HttpClient) { 20 | this.readContinents2(); 21 | } 22 | 23 | public readContinents2() { 24 | console.log("Reading continents from web") 25 | this.countries$ = this.httpClient.get>('https://restcountries.com/v2/all').pipe(tap(x => console.log("Read", x))); 26 | this.continents$ = this.countries$.pipe(map((countries) => this.extractContinents(countries))); 27 | this.listOfContinents$ = this.countries$.pipe(map((countries) => this.extractContinentNames(countries))); 28 | } 29 | 30 | private extractContinentNames(countries: Array): Array { 31 | const listOfNames = countries.map((country) => country.region); 32 | const listOfNonEmptyNames = listOfNames.filter((continent) => continent !== ''); 33 | const uniqueNames = new Set(listOfNonEmptyNames); 34 | const arrayOfUniqueNames = [...uniqueNames]; 35 | return arrayOfUniqueNames; 36 | } 37 | 38 | private extractContinents(countries: Array): Array { 39 | const continentNames = this.extractContinentNames(countries); 40 | const continents = continentNames.map((name) => ({ 41 | name, 42 | countriesInContinent: countries.filter((country) => country.region === name), 43 | })); 44 | return continents; 45 | } 46 | 47 | public save(modifiedCountry: Country): void { 48 | console.log("todo: implement save()"); 49 | console.log(`Country to save: ${JSON.stringify(modifiedCountry)}`); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/edit-country.component.html: -------------------------------------------------------------------------------- 1 | 8 |
9 |
10 |
11 |

{{ (country$ | async)?.name }}

12 | 13 |
Name
14 |
15 | 16 |
17 | 21 | The field is required. 22 | 23 | 24 |
Population
25 |
26 | 27 |
28 | 32 | The field is required. 33 | 34 | 35 |
Region
36 |
37 | 38 |
39 | 43 | The field is required. 44 | 45 | 49 | The minimum length for this field is 50 | {{ 51 | formGroup.get("region")?.errors?.minlength.requiredLength 52 | }} 53 | characters. 54 | 55 | 56 |
Flag
57 |
58 | 59 |
60 | 64 | The field is required. 65 | 66 |
67 | 70 |
71 |
72 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/edit-country.component.class.spec.ts: -------------------------------------------------------------------------------- 1 | import { ConfirmationService } from 'primeng/api'; 2 | import { EditCountryComponent } from './edit-country.component'; 3 | import { mockActivateRouteValue } from './activated-route-mock'; 4 | import { MockService } from 'ng-mocks'; 5 | import { ContinentService } from '../continent-service/continent.service'; 6 | import { of } from 'rxjs'; 7 | import { Country } from '../continent-service/country'; 8 | 9 | describe('EditCountryComponent', () => { 10 | let component: EditCountryComponent; 11 | 12 | beforeEach(() => { 13 | let service = MockService(ContinentService); 14 | service.countries$ = of([{ name: 'Spain', population: 0, flag: '', region: 'Europe' }]); 15 | component = new EditCountryComponent(mockActivateRouteValue, service, MockService(ConfirmationService)); 16 | }); 17 | 18 | it('should create', () => { 19 | expect(component).toBeTruthy(); 20 | }); 21 | 22 | it('should update on navigation', () => { 23 | expect(component.formGroup.value.name).toBe(""); 24 | component.ngOnInit(); 25 | component.country$.subscribe(); 26 | console.log(component.formGroup.value); 27 | expect(component.formGroup.value.name).toBe("Spain"); 28 | }); 29 | 30 | it('should validate the name', () => { 31 | component.formGroup.patchValue({ name: '' }); 32 | expect(component.formGroup.controls.name.valid).toBeFalsy(); 33 | component.formGroup.patchValue({ name: 'Bob' }); 34 | expect(component.formGroup.controls.name.valid).toBeTruthy(); 35 | component.formGroup.patchValue({ name: '' }); 36 | expect(component.formGroup.controls.name.valid).toBeFalsy(); 37 | }); 38 | 39 | it.each([ 40 | ['name', 'Spain'], 41 | ['population', '46940000'], 42 | ['region', 'Europe'], 43 | ['flag', 'https://example.com/flag-of-spain.gif'], 44 | ])(`should validate the %p field`, (name, value) => { 45 | const empty = {}; 46 | empty[name] = ''; 47 | const withValue = {}; 48 | withValue[name] = value; 49 | component.formGroup.patchValue(empty); 50 | expect(component.formGroup.controls[name].valid).toBeFalsy(); 51 | component.formGroup.patchValue(withValue); 52 | expect(component.formGroup.controls[name].valid).toBeTruthy(); 53 | component.formGroup.patchValue(empty); 54 | expect(component.formGroup.controls[name].valid).toBeFalsy(); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /Angular17/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "Angular17": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:application", 15 | "options": { 16 | "outputPath": "dist/angular17", 17 | "index": "src/index.html", 18 | "browser": "src/main.ts", 19 | "polyfills": [ 20 | "zone.js" 21 | ], 22 | "tsConfig": "tsconfig.app.json", 23 | "assets": [ 24 | "src/favicon.ico", 25 | "src/assets" 26 | ], 27 | "styles": [ 28 | "src/styles.css" 29 | ], 30 | "scripts": [] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "budgets": [ 35 | { 36 | "type": "initial", 37 | "maximumWarning": "500kb", 38 | "maximumError": "1mb" 39 | }, 40 | { 41 | "type": "anyComponentStyle", 42 | "maximumWarning": "2kb", 43 | "maximumError": "4kb" 44 | } 45 | ], 46 | "outputHashing": "all" 47 | }, 48 | "development": { 49 | "optimization": false, 50 | "extractLicenses": false, 51 | "sourceMap": true 52 | } 53 | }, 54 | "defaultConfiguration": "production" 55 | }, 56 | "serve": { 57 | "builder": "@angular-devkit/build-angular:dev-server", 58 | "configurations": { 59 | "production": { 60 | "buildTarget": "Angular17:build:production" 61 | }, 62 | "development": { 63 | "buildTarget": "Angular17:build:development" 64 | } 65 | }, 66 | "defaultConfiguration": "development" 67 | }, 68 | "extract-i18n": { 69 | "builder": "@angular-devkit/build-angular:extract-i18n", 70 | "options": { 71 | "buildTarget": "Angular17:build" 72 | } 73 | }, 74 | "test": { 75 | "builder": "@angular-devkit/build-angular:jest", 76 | "options": { 77 | "polyfills": [ 78 | "zone.js", 79 | "zone.js/testing" 80 | ], 81 | "tsConfig": "tsconfig.spec.json" 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/edit-country.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { ActivatedRoute } from '@angular/router'; 4 | import { ConfirmationService } from 'primeng/api'; 5 | import { Observable } from 'rxjs'; 6 | import { map, switchMap, tap } from 'rxjs/operators'; 7 | import { ContinentService } from '../continent-service/continent.service'; 8 | import { Country } from '../continent-service/country'; 9 | 10 | @Component({ 11 | selector: 'app-edit-country', 12 | templateUrl: './edit-country.component.html', 13 | styleUrls: ['./edit-country.component.scss'], 14 | }) 15 | export class EditCountryComponent implements OnInit { 16 | public country$!: Observable; 17 | 18 | public formGroup: FormGroup = new FormGroup({ 19 | name: new FormControl('', Validators.required), 20 | population: new FormControl('', Validators.required), 21 | region: new FormControl('', [Validators.required, Validators.minLength(4)]), 22 | flag: new FormControl('', Validators.required), 23 | }); 24 | 25 | public canSave$ = this.formGroup.statusChanges.pipe(map(() => this.formGroup.valid)); 26 | 27 | constructor(private route: ActivatedRoute, private continentService: ContinentService, private confirmation: ConfirmationService) {} 28 | 29 | public ngOnInit(): void { 30 | this.country$ = this.route.params.pipe( 31 | map((parameters) => parameters['country']), 32 | map((countryName) => this.findCountryFromContinentService(countryName)), 33 | switchMap((country$) => country$), 34 | 35 | tap((country) => this.fillInData(country)) 36 | ); 37 | } 38 | 39 | private fillInData(country: Country | undefined): void { 40 | console.log('Fill in data'); 41 | this.formGroup.get('name')?.patchValue(country?.name || 'unknown'); 42 | this.formGroup.get('region')?.patchValue(country?.region || 'unknown'); 43 | this.formGroup.get('population')?.patchValue(country?.population || 'unknown'); 44 | this.formGroup.get('flag')?.patchValue(country?.flag || 'unknown'); 45 | } 46 | 47 | private findCountryFromContinentService(countryName: string): Observable { 48 | return this.continentService.countries$.pipe(map((countries) => this.findCountry(countries, countryName))); 49 | } 50 | 51 | private findCountry(countries: Country[], countryName: string): Country | undefined { 52 | console.log('Find country'); 53 | const country = countries.find((c) => c.name === countryName); 54 | return country; 55 | } 56 | 57 | public save(): void { 58 | this.confirmation.confirm({ 59 | key: 'confirm-save-country', 60 | message: 'Do you want to save the country?', 61 | accept: () => { 62 | this.doSave(); 63 | }, 64 | }); 65 | } 66 | 67 | public doSave(): void { 68 | this.continentService.save(this.formGroup.value as Country); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/__snapshots__/save-country-with-dummy-component.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Save-Country-Question Should render the confirmation dialog 1`] = ` 4 | 5 |
8 | 13 |
16 |
20 | 21 |
25 | 26 |
29 | 37 | 38 |
39 |
40 | 41 |
44 | 45 | 48 | Do you want to save the country? 49 | 50 |
51 | 52 | 91 | 92 |
93 | 94 |
95 | 96 |
97 |
98 | 99 | `; 100 | -------------------------------------------------------------------------------- /Angular 15 and below/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * IE11 requires the following for NgClass support on SVG elements 23 | */ 24 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 25 | 26 | /** 27 | * Web Animations `@angular/platform-browser/animations` 28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 30 | */ 31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 32 | 33 | /** 34 | * By default, zone.js will patch all possible macroTask and DomEvents 35 | * user can disable parts of macroTask/DomEvents patch by setting following flags 36 | * because those flags need to be set before `zone.js` being loaded, and webpack 37 | * will put import in the top of bundle, so user need to create a separate file 38 | * in this directory (for example: zone-flags.ts), and put the following flags 39 | * into that file, and then add the following code before importing zone.js. 40 | * import './zone-flags'; 41 | * 42 | * The flags allowed in zone-flags.ts are listed here. 43 | * 44 | * The following flags will work for all browsers. 45 | * 46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 49 | * 50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 52 | * 53 | * (window as any).__Zone_enable_cross_context_check = true; 54 | * 55 | */ 56 | 57 | /*************************************************************************************************** 58 | * Zone JS is required by default for Angular itself. 59 | */ 60 | import 'zone.js'; // Included with Angular CLI. 61 | 62 | /*************************************************************************************************** 63 | * APPLICATION IMPORTS 64 | */ 65 | -------------------------------------------------------------------------------- /Angular 15 and below/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "example-app-v12": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:application": { 10 | "strict": true 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/example-app-v12", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.css", 31 | "node_modules/primeng/resources/themes/mdc-light-indigo/theme.css", 32 | "node_modules/primeng/resources/primeng.min.css" 33 | ], 34 | "scripts": [], 35 | "vendorChunk": true, 36 | "extractLicenses": false, 37 | "buildOptimizer": false, 38 | "sourceMap": true, 39 | "optimization": false, 40 | "namedChunks": true 41 | }, 42 | "configurations": { 43 | "production": { 44 | "fileReplacements": [ 45 | { 46 | "replace": "src/environments/environment.ts", 47 | "with": "src/environments/environment.prod.ts" 48 | } 49 | ], 50 | "optimization": true, 51 | "outputHashing": "all", 52 | "sourceMap": false, 53 | "namedChunks": false, 54 | "extractLicenses": true, 55 | "vendorChunk": false, 56 | "buildOptimizer": true, 57 | "budgets": [ 58 | { 59 | "type": "initial", 60 | "maximumWarning": "500kb", 61 | "maximumError": "1mb" 62 | }, 63 | { 64 | "type": "anyComponentStyle", 65 | "maximumWarning": "2kb", 66 | "maximumError": "4kb" 67 | } 68 | ] 69 | } 70 | } 71 | }, 72 | "serve": { 73 | "builder": "@angular-devkit/build-angular:dev-server", 74 | "options": { 75 | "browserTarget": "example-app-v12:build" 76 | }, 77 | "configurations": { 78 | "production": { 79 | "browserTarget": "example-app-v12:build:production" 80 | } 81 | } 82 | }, 83 | "extract-i18n": { 84 | "builder": "@angular-devkit/build-angular:extract-i18n", 85 | "options": { 86 | "browserTarget": "example-app-v12:build" 87 | } 88 | } 89 | } 90 | } 91 | }, 92 | "defaultProject": "example-app-v12", 93 | "cli": { 94 | "packageManager": "yarn", 95 | "analytics": false 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/edit-country.component.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ConfirmationService } from 'primeng/api'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { EditCountryComponent } from './edit-country.component'; 5 | import { mockActivatedRoute } from './activated-route-mock'; 6 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 7 | import { ReactiveFormsModule } from '@angular/forms'; 8 | import { ContinentService } from '../continent-service/continent.service'; 9 | import { MockComponent, MockService } from 'ng-mocks'; 10 | import { of } from 'rxjs'; 11 | import { Country } from '../continent-service/country'; 12 | import { ConfirmDialog } from 'primeng/confirmdialog'; 13 | 14 | describe('EditCountryComponent', () => { 15 | let component: EditCountryComponent; 16 | let fixture: ComponentFixture; 17 | let nameInputField: HTMLInputElement; 18 | let saveButton: HTMLButtonElement; 19 | let MockContinentService: ContinentService; 20 | 21 | beforeEach(async () => { 22 | MockContinentService = MockService(ContinentService); 23 | MockContinentService.countries$ = of>([{name: "Spain", population: 0, region: "Europe", flag: undefined}, 24 | {name: "Portugal", population: 0, region: "Europe", flag: undefined}]) 25 | ; 26 | MockContinentService.save = jest.fn(); 27 | await TestBed.configureTestingModule({ 28 | declarations: [EditCountryComponent, MockComponent(ConfirmDialog)], 29 | providers: [mockActivatedRoute, { provide: ContinentService, useValue: MockContinentService}, 30 | ConfirmationService], 31 | imports: [HttpClientTestingModule, ReactiveFormsModule], 32 | }).compileComponents(); 33 | }); 34 | 35 | beforeEach(async () => { 36 | fixture = TestBed.createComponent(EditCountryComponent); 37 | component = fixture.componentInstance; 38 | 39 | fixture.detectChanges(); // ngOnInit() 40 | // await fixture.whenStable(); 41 | nameInputField = fixture.nativeElement.querySelector('input[name="name"]') as HTMLInputElement; 42 | saveButton = fixture.nativeElement.querySelector('button') as HTMLButtonElement; 43 | }); 44 | 45 | it('should create', () => { 46 | expect(component).toBeTruthy(); 47 | }); 48 | 49 | it('should update on navigation', async () => { 50 | fixture.detectChanges(); 51 | component.country$.subscribe(); 52 | // await component.country$.toPromise(); // <<< this does't work 53 | // await fixture.whenStable(); 54 | expect(component.formGroup.value.name).toBe("Spain") 55 | }); 56 | 57 | it('should store the input value in the formGroup', async () => { 58 | expect(nameInputField).toBeTruthy(); 59 | nameInputField.value = 'Portugal'; 60 | nameInputField.dispatchEvent(new Event('input')); 61 | fixture.detectChanges(); 62 | // await fixture.whenStable(); 63 | expect(component.formGroup.get('name').value).toBe('Portugal'); 64 | }); 65 | 66 | it('should save the form data', async () => { 67 | expect(nameInputField).toBeTruthy(); 68 | nameInputField.value = 'Portugal'; 69 | nameInputField.dispatchEvent(new Event('input')); 70 | fixture.detectChanges(); 71 | expect(component.formGroup.get('name').value).toBe('Portugal'); 72 | saveButton.click(); 73 | await fixture.whenStable(); 74 | expect(MockContinentService.save).not.toBeCalled(); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /angular17-workspace/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "app1": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "projects/app1", 10 | "sourceRoot": "projects/app1/src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:application", 15 | "options": { 16 | "outputPath": "dist/app1", 17 | "index": "projects/app1/src/index.html", 18 | "browser": "projects/app1/src/main.ts", 19 | "polyfills": [ 20 | "zone.js" 21 | ], 22 | "tsConfig": "projects/app1/tsconfig.app.json", 23 | "assets": [ 24 | "projects/app1/src/favicon.ico", 25 | "projects/app1/src/assets" 26 | ], 27 | "styles": [ 28 | "projects/app1/src/styles.css" 29 | ], 30 | "scripts": [] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "budgets": [ 35 | { 36 | "type": "initial", 37 | "maximumWarning": "500kb", 38 | "maximumError": "1mb" 39 | }, 40 | { 41 | "type": "anyComponentStyle", 42 | "maximumWarning": "2kb", 43 | "maximumError": "4kb" 44 | } 45 | ], 46 | "outputHashing": "all" 47 | }, 48 | "development": { 49 | "optimization": false, 50 | "extractLicenses": false, 51 | "sourceMap": true 52 | } 53 | }, 54 | "defaultConfiguration": "production" 55 | }, 56 | "serve": { 57 | "builder": "@angular-devkit/build-angular:dev-server", 58 | "configurations": { 59 | "production": { 60 | "buildTarget": "app1:build:production" 61 | }, 62 | "development": { 63 | "buildTarget": "app1:build:development" 64 | } 65 | }, 66 | "defaultConfiguration": "development" 67 | }, 68 | "extract-i18n": { 69 | "builder": "@angular-devkit/build-angular:extract-i18n", 70 | "options": { 71 | "buildTarget": "app1:build" 72 | } 73 | } 74 | } 75 | }, 76 | "app2": { 77 | "projectType": "application", 78 | "schematics": {}, 79 | "root": "projects/app2", 80 | "sourceRoot": "projects/app2/src", 81 | "prefix": "app", 82 | "architect": { 83 | "build": { 84 | "builder": "@angular-devkit/build-angular:application", 85 | "options": { 86 | "outputPath": "dist/app2", 87 | "index": "projects/app2/src/index.html", 88 | "browser": "projects/app2/src/main.ts", 89 | "polyfills": [ 90 | "zone.js" 91 | ], 92 | "tsConfig": "projects/app2/tsconfig.app.json", 93 | "assets": [ 94 | "projects/app2/src/favicon.ico", 95 | "projects/app2/src/assets" 96 | ], 97 | "styles": [ 98 | "projects/app2/src/styles.css" 99 | ], 100 | "scripts": [] 101 | }, 102 | "configurations": { 103 | "production": { 104 | "budgets": [ 105 | { 106 | "type": "initial", 107 | "maximumWarning": "500kb", 108 | "maximumError": "1mb" 109 | }, 110 | { 111 | "type": "anyComponentStyle", 112 | "maximumWarning": "2kb", 113 | "maximumError": "4kb" 114 | } 115 | ], 116 | "outputHashing": "all" 117 | }, 118 | "development": { 119 | "optimization": false, 120 | "extractLicenses": false, 121 | "sourceMap": true 122 | } 123 | }, 124 | "defaultConfiguration": "production" 125 | }, 126 | "serve": { 127 | "builder": "@angular-devkit/build-angular:dev-server", 128 | "configurations": { 129 | "production": { 130 | "buildTarget": "app2:build:production" 131 | }, 132 | "development": { 133 | "buildTarget": "app2:build:development" 134 | } 135 | }, 136 | "defaultConfiguration": "development" 137 | }, 138 | "extract-i18n": { 139 | "builder": "@angular-devkit/build-angular:extract-i18n", 140 | "options": { 141 | "buildTarget": "app2:build" 142 | } 143 | } 144 | } 145 | }, 146 | "shared-library": { 147 | "projectType": "library", 148 | "root": "projects/shared-library", 149 | "sourceRoot": "projects/shared-library/src", 150 | "prefix": "lib", 151 | "architect": { 152 | "build": { 153 | "builder": "@angular-devkit/build-angular:ng-packagr", 154 | "options": { 155 | "project": "projects/shared-library/ng-package.json" 156 | }, 157 | "configurations": { 158 | "production": { 159 | "tsConfig": "projects/shared-library/tsconfig.lib.prod.json" 160 | }, 161 | "development": { 162 | "tsConfig": "projects/shared-library/tsconfig.lib.json" 163 | } 164 | }, 165 | "defaultConfiguration": "production" 166 | } 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Angular 15 and below/src/app/edit-country/__snapshots__/save-country.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Save-Country-Question Should render the confirmation dialog 1`] = ` 4 | 5 |
8 | 16 |
19 |
23 | 24 |
28 | 31 | Save country 32 | 33 | 34 |
37 | 45 | 46 |
47 |
48 | 49 |
52 | 53 | 56 | Do you want to save the country? 57 | 58 |
59 | 60 | 99 | 100 |
101 | 102 |
103 | 104 |
105 |
108 |
109 |
112 |

115 | 116 |

117 |
120 | Name 121 |
122 |
125 | 130 |
131 | 134 | The field is required. 135 | 136 | 137 |
140 | Population 141 |
142 |
145 | 150 |
151 | 154 | The field is required. 155 | 156 | 157 |
160 | Region 161 |
162 |
165 | 170 |
171 | 174 | The field is required. 175 | 176 | 177 | 178 |
181 | Flag 182 |
183 |
186 | 191 |
192 | 195 | The field is required. 196 | 197 | 198 |
199 | 206 |
207 |
208 |
209 | 210 | `; 211 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "app1": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "projects/app1", 10 | "sourceRoot": "projects/app1/src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:application", 15 | "options": { 16 | "outputPath": "dist/app1", 17 | "index": "projects/app1/src/index.html", 18 | "browser": "projects/app1/src/main.ts", 19 | "polyfills": [ 20 | "zone.js" 21 | ], 22 | "tsConfig": "projects/app1/tsconfig.app.json", 23 | "assets": [ 24 | "projects/app1/src/favicon.ico", 25 | "projects/app1/src/assets" 26 | ], 27 | "styles": [ 28 | "projects/app1/src/styles.css" 29 | ], 30 | "scripts": [] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "budgets": [ 35 | { 36 | "type": "initial", 37 | "maximumWarning": "500kb", 38 | "maximumError": "1mb" 39 | }, 40 | { 41 | "type": "anyComponentStyle", 42 | "maximumWarning": "2kb", 43 | "maximumError": "4kb" 44 | } 45 | ], 46 | "outputHashing": "all" 47 | }, 48 | "development": { 49 | "optimization": false, 50 | "extractLicenses": false, 51 | "sourceMap": true 52 | } 53 | }, 54 | "defaultConfiguration": "production" 55 | }, 56 | "serve": { 57 | "builder": "@angular-devkit/build-angular:dev-server", 58 | "configurations": { 59 | "production": { 60 | "buildTarget": "app1:build:production" 61 | }, 62 | "development": { 63 | "buildTarget": "app1:build:development" 64 | } 65 | }, 66 | "defaultConfiguration": "development" 67 | }, 68 | "extract-i18n": { 69 | "builder": "@angular-devkit/build-angular:extract-i18n", 70 | "options": { 71 | "buildTarget": "app1:build" 72 | } 73 | }, 74 | "test": { 75 | "builder": "@angular-builders/jest:run", 76 | "options": { 77 | "polyfills": [ 78 | "zone.js", 79 | "zone.js/testing" 80 | ], 81 | "modulePathIgnorePatterns": [ 82 | "/projects/shared-library/" 83 | ] 84 | } 85 | } 86 | } 87 | }, 88 | "app2": { 89 | "projectType": "application", 90 | "schematics": {}, 91 | "root": "projects/app2", 92 | "sourceRoot": "projects/app2/src", 93 | "prefix": "app", 94 | "architect": { 95 | "build": { 96 | "builder": "@angular-devkit/build-angular:application", 97 | "options": { 98 | "outputPath": "dist/app2", 99 | "index": "projects/app2/src/index.html", 100 | "browser": "projects/app2/src/main.ts", 101 | "polyfills": [ 102 | "zone.js" 103 | ], 104 | "tsConfig": "projects/app2/tsconfig.app.json", 105 | "assets": [ 106 | "projects/app2/src/favicon.ico", 107 | "projects/app2/src/assets" 108 | ], 109 | "styles": [ 110 | "projects/app2/src/styles.css" 111 | ], 112 | "scripts": [] 113 | }, 114 | "configurations": { 115 | "production": { 116 | "budgets": [ 117 | { 118 | "type": "initial", 119 | "maximumWarning": "500kb", 120 | "maximumError": "1mb" 121 | }, 122 | { 123 | "type": "anyComponentStyle", 124 | "maximumWarning": "2kb", 125 | "maximumError": "4kb" 126 | } 127 | ], 128 | "outputHashing": "all" 129 | }, 130 | "development": { 131 | "optimization": false, 132 | "extractLicenses": false, 133 | "sourceMap": true 134 | } 135 | }, 136 | "defaultConfiguration": "production" 137 | }, 138 | "serve": { 139 | "builder": "@angular-devkit/build-angular:dev-server", 140 | "configurations": { 141 | "production": { 142 | "buildTarget": "app2:build:production" 143 | }, 144 | "development": { 145 | "buildTarget": "app2:build:development" 146 | } 147 | }, 148 | "defaultConfiguration": "development" 149 | }, 150 | "extract-i18n": { 151 | "builder": "@angular-devkit/build-angular:extract-i18n", 152 | "options": { 153 | "buildTarget": "app2:build" 154 | } 155 | }, 156 | "test": { 157 | "builder": "@angular-builders/jest:run", 158 | "options": { 159 | "polyfills": [ 160 | "zone.js", 161 | "zone.js/testing" 162 | ], 163 | "modulePathIgnorePatterns": [ 164 | "/projects/shared-library/" 165 | ] 166 | } 167 | } 168 | } 169 | }, 170 | "shared-library": { 171 | "projectType": "library", 172 | "root": "projects/shared-library", 173 | "sourceRoot": "projects/shared-library/src", 174 | "prefix": "lib", 175 | "architect": { 176 | "build": { 177 | "builder": "@angular-devkit/build-angular:ng-packagr", 178 | "options": { 179 | "project": "projects/shared-library/ng-package.json" 180 | }, 181 | "configurations": { 182 | "production": { 183 | "tsConfig": "projects/shared-library/tsconfig.lib.prod.json" 184 | }, 185 | "development": { 186 | "tsConfig": "projects/shared-library/tsconfig.lib.json" 187 | } 188 | }, 189 | "defaultConfiguration": "production" 190 | }, 191 | "test": { 192 | "builder": "@angular-builders/jest:run", 193 | "options": { 194 | "polyfills": [ 195 | "zone.js", 196 | "zone.js/testing" 197 | ] 198 | } 199 | } 200 | } 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Angular 15 and below/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Angular17/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 179 | 180 |
181 |
182 |
183 | 228 |

Hello, {{ title }}

229 |

Congratulations! Your app is running. 🎉

230 |
231 | 232 |
233 |
234 | @for (item of [ 235 | { title: 'Explore the Docs', link: 'https://angular.dev' }, 236 | { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, 237 | { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, 238 | { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, 239 | { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, 240 | ]; track item.title) { 241 | 247 | {{ item.title }} 248 | 255 | 258 | 259 | 260 | } 261 |
262 | 323 |
324 |
325 |
326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /angular17-workspace/projects/app2/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 179 | 180 |
181 |
182 |
183 | 228 |

Hello, {{ title }}

229 |

Congratulations! Your app is running. 🎉

230 |
231 | 232 |
233 |
234 | @for (item of [ 235 | { title: 'Explore the Docs', link: 'https://angular.dev' }, 236 | { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, 237 | { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, 238 | { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, 239 | { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, 240 | ]; track item.title) { 241 | 247 | {{ item.title }} 248 | 255 | 258 | 259 | 260 | } 261 |
262 | 323 |
324 |
325 |
326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /angular17-JustJeb-workspace/projects/app2/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 179 | 180 |
181 |
182 |
183 | 228 |

Hello, {{ title }}

229 |

Congratulations! Your app is running. 🎉

230 |
231 | 232 |
233 |
234 | @for (item of [ 235 | { title: 'Explore the Docs', link: 'https://angular.dev' }, 236 | { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, 237 | { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, 238 | { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, 239 | { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, 240 | ]; track item.title) { 241 | 247 | {{ item.title }} 248 | 255 | 258 | 259 | 260 | } 261 |
262 | 323 |
324 |
325 |
326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | --------------------------------------------------------------------------------