├── .github └── workflows │ └── angular-tdd.yml ├── .gitignore ├── Chapter 1 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ └── app.module.ts │ ├── assets │ │ └── .gitkeep │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 11 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ ├── calculator.cy.ts │ │ └── spec.cy.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ ├── pipes │ │ │ ├── percent.pipe.spec.ts │ │ │ └── percent.pipe.ts │ │ └── services │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ ├── favicon.ico │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── styles.scss │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 2 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 3 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ └── services │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 4 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ ├── mocks │ │ │ └── mock-square-root.service.mock.ts │ │ ├── services │ │ │ ├── calculator-async.service.spec.ts │ │ │ ├── calculator-async.service.ts │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ │ └── stubs │ │ │ └── calculator.service.stub.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 5 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ ├── pipes │ │ │ ├── percent.pipe.spec.ts │ │ │ └── percent.pipe.ts │ │ └── services │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 7 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ └── spec.cy.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ ├── pipes │ │ │ ├── percent.pipe.spec.ts │ │ │ └── percent.pipe.ts │ │ └── services │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 8 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ ├── calculator.cy.ts │ │ └── spec.cy.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ ├── pipes │ │ │ ├── percent.pipe.spec.ts │ │ │ └── percent.pipe.ts │ │ └── services │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── Chapter 9 └── getting-started-angular-tdd │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ ├── calculator.cy.ts │ │ └── spec.cy.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── calculator │ │ │ ├── calculator-routing.module.ts │ │ │ ├── calculator.component.html │ │ │ ├── calculator.component.scss │ │ │ ├── calculator.component.spec.ts │ │ │ ├── calculator.component.ts │ │ │ └── calculator.module.ts │ ├── assets │ │ └── .gitkeep │ ├── core │ │ ├── directives │ │ │ ├── color-change.directive.spec.ts │ │ │ └── color-change.directive.ts │ │ ├── pipes │ │ │ ├── percent.pipe.spec.ts │ │ │ └── percent.pipe.ts │ │ └── services │ │ │ ├── calculator.service.spec.ts │ │ │ └── calculator.service.ts │ ├── favicon.ico │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── styles.scss │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json └── README.md /.github/workflows/angular-tdd.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Angular TDD CI/CD 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | test-and-build: 14 | 15 | runs-on: ubuntu-latest 16 | defaults: 17 | run: 18 | working-directory: './Chapter 9/getting-started-angular-tdd/' 19 | 20 | strategy: 21 | matrix: 22 | node-version: [18.x] 23 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 24 | 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: Use Node.js ${{ matrix.node-version }} 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | cache: 'npm' 32 | cache-dependency-path: '**/package-lock.json' 33 | - run: npm ci 34 | - run: npm run test -- --configuration=ci 35 | - run: npm run build --if-present -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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": "^16.1.0", 14 | "@angular/common": "^16.1.0", 15 | "@angular/compiler": "^16.1.0", 16 | "@angular/core": "^16.1.0", 17 | "@angular/forms": "^16.1.0", 18 | "@angular/platform-browser": "^16.1.0", 19 | "@angular/platform-browser-dynamic": "^16.1.0", 20 | "@angular/router": "^16.1.0", 21 | "rxjs": "~7.8.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.13.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^16.1.3", 27 | "@angular/cli": "~16.1.3", 28 | "@angular/compiler-cli": "^16.1.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.6.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.2.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.1.0", 36 | "typescript": "~5.1.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/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 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class AppRoutingModule { } 11 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 1/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 1/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 1/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 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 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Chapter 1/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('template spec', () => { 2 | it('passes', () => { 3 | cy.visit('https://example.cypress.io') 4 | }) 5 | }) -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } 38 | 39 | Cypress.Commands.add( 40 | 'performCalculation', 41 | 42 | (firstNumber, operator, secondNumber) => { 43 | cy.get('input').first().type(firstNumber); 44 | cy.get('select').select(operator).should('have.value', operator); 45 | cy.get('input').last().type(secondNumber); 46 | cy.get('button').click(); 47 | } 48 | ); 49 | 50 | declare namespace Cypress { 51 | interface Chainable { 52 | performCalculation( 53 | firstNumber: string, 54 | operator: string, 55 | secondNumber: string 56 | ): Chainable; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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 | "cypress:open": "cypress open" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^16.1.0", 15 | "@angular/common": "^16.1.0", 16 | "@angular/compiler": "^16.1.0", 17 | "@angular/core": "^16.1.0", 18 | "@angular/forms": "^16.1.0", 19 | "@angular/platform-browser": "^16.1.0", 20 | "@angular/platform-browser-dynamic": "^16.1.0", 21 | "@angular/router": "^16.1.0", 22 | "rxjs": "~7.8.0", 23 | "tslib": "^2.3.0", 24 | "zone.js": "~0.13.0" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "^16.1.3", 28 | "@angular/cli": "~16.1.3", 29 | "@angular/compiler-cli": "^16.1.0", 30 | "@types/jasmine": "~4.3.0", 31 | "cypress": "^13.6.2", 32 | "jasmine-core": "~4.6.0", 33 | "karma": "~6.4.0", 34 | "karma-chrome-launcher": "~3.2.0", 35 | "karma-coverage": "~2.2.0", 36 | "karma-coverage-istanbul-reporter": "^3.0.3", 37 | "karma-jasmine": "~5.1.0", 38 | "karma-jasmine-html-reporter": "~2.1.0", 39 | "puppeteer": "^22.3.0", 40 | "typescript": "~5.1.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/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 | { 6 | path: '', 7 | loadChildren: () => 8 | import('./calculator/calculator.module').then((m) => m.CalculatorModule), 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forRoot(routes)], 14 | exports: [RouterModule], 15 | }) 16 | export class AppRoutingModule {} 17 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 11/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | // const fixture = TestBed.createComponent(AppComponent); 19 | // const app = fixture.componentInstance; 20 | // expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | // }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 10 | 13 |

{{ result }}

14 |
15 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 11/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { CalculatorService } from 'src/core/services/calculator.service'; 4 | 5 | @Component({ 6 | selector: 'app-calculator', 7 | templateUrl: './calculator.component.html', 8 | styleUrls: ['./calculator.component.scss'], 9 | }) 10 | export class CalculatorComponent implements OnInit { 11 | result!: number; 12 | color = 'red'; 13 | calculatorForm!: FormGroup; 14 | 15 | constructor(private calculatorService: CalculatorService) { 16 | this.calculatorForm = new FormGroup({ 17 | operand1: new FormControl(null, [Validators.required]), 18 | operand2: new FormControl(null, [Validators.required]), 19 | operator: new FormControl(null, [Validators.required]), 20 | }); 21 | } 22 | 23 | ngOnInit(): void { 24 | this.result = 0; 25 | } 26 | 27 | add(a: number, b: number): void { 28 | this.result = this.calculatorService.add(a, b); 29 | } 30 | 31 | substract(a: number, b: number): void { 32 | this.result = this.calculatorService.substract(a, b); 33 | } 34 | 35 | multiply(a: number, b: number): void { 36 | this.result = this.calculatorService.multiply(a, b); 37 | } 38 | 39 | divide(a: number, b: number): void { 40 | this.result = this.calculatorService.divide(a, b); 41 | } 42 | 43 | calculate(): void { 44 | const operator = this.calculatorForm.get('operator')?.value; 45 | const operand1 = this.calculatorForm.get('operand1')?.value; 46 | const operand2 = this.calculatorForm.get('operand2')?.value; 47 | 48 | if (!operator || !operand1 || !operand2) return; // Early exit if any required value is missing 49 | 50 | switch (operator) { 51 | case '+': 52 | this.add(operand1, operand2); 53 | break; 54 | case '-': 55 | this.substract(operand1, operand2); 56 | break; 57 | case '*': 58 | this.multiply(operand1, operand2); 59 | break; 60 | case '/': 61 | this.divide(operand1, operand2); 62 | break; 63 | default: 64 | console.error(`Unsupported operator: ${operator}`); 65 | break; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | import { PercentPipe } from 'src/core/pipes/percent.pipe'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | 10 | 11 | @NgModule({ 12 | declarations: [ 13 | CalculatorComponent, 14 | ColorChangeDirective, 15 | PercentPipe 16 | ], 17 | imports: [ 18 | CommonModule, 19 | ReactiveFormsModule, 20 | CalculatorRoutingModule 21 | ] 22 | }) 23 | export class CalculatorModule { } 24 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 11/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | 7 | describe('ColorChangeDirective', () => { 8 | let fixture: ComponentFixture; 9 | let calculator: CalculatorComponent; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [ColorChangeDirective, CalculatorComponent], 14 | imports: [ReactiveFormsModule] 15 | }).compileComponents(); 16 | 17 | fixture = TestBed.createComponent(CalculatorComponent); 18 | calculator = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should apply the specified color', () => { 23 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 24 | const color: string = 'red'; 25 | calculator.color = color; 26 | fixture.detectChanges(); 27 | 28 | expect(element.style.color).toBe(color); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/core/pipes/percent.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { PercentPipe } from './percent.pipe'; 2 | 3 | describe('PercentPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new PercentPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | 9 | it('should format a positive number to a percentage string', () => { 10 | const input = 123; 11 | const output = new PercentPipe().transform(input); 12 | expect(output).toBe('12300%'); 13 | }); 14 | 15 | it('should format a negative number to a percentage string', () => { 16 | const input = -123; 17 | const output = new PercentPipe().transform(input); 18 | expect(output).toBe('-12300%'); 19 | }); 20 | 21 | it('should format a decimal number to a percentage string', () => { 22 | const input = 123.45; 23 | const output = new PercentPipe().transform(input); 24 | expect(output).toBe('12345%'); 25 | }); 26 | 27 | it('should format a zero number to a percentage string', () => { 28 | const input = 0; 29 | const output = new PercentPipe().transform(input); 30 | expect(output).toBe('0%'); 31 | }); 32 | 33 | it('should format a zero number to a percentage string', () => { 34 | const input = NaN; 35 | const output = new PercentPipe().transform(input); 36 | expect(output).toBe('Error'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/core/pipes/percent.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'percent', 5 | }) 6 | export class PercentPipe implements PipeTransform { 7 | 8 | transform(value: number): string { 9 | if (isNaN(value)) { 10 | return 'Error'; 11 | } 12 | 13 | const formattedValue = value * 100; 14 | return formattedValue + '%'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() { } 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number { 26 | return a / b; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 11/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | process.env.CHROME_BIN = require("puppeteer").executablePath(); 5 | 6 | module.exports = function (config) { 7 | config.set({ 8 | basePath: "", 9 | frameworks: ["jasmine", "@angular-devkit/build-angular"], 10 | plugins: [ 11 | require("karma-jasmine"), 12 | require("karma-chrome-launcher"), 13 | require("karma-jasmine-html-reporter"), 14 | require("karma-coverage-istanbul-reporter"), 15 | require("@angular-devkit/build-angular/plugins/karma"), 16 | ], 17 | client: { 18 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 19 | }, 20 | coverageIstanbulReporter: { 21 | dir: require("path").join(__dirname, "../coverage"), 22 | reports: ["html", "lcovonly"], 23 | fixWebpackSourcePaths: true, 24 | }, 25 | reporters: ["progress", "kjhtml"], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ["Chrome"], 31 | customLaunchers: { 32 | ChromeHeadlessCI: { 33 | base: "ChromeHeadless", 34 | flags: ["--no-sandbox", "--disable-gpu"], 35 | }, 36 | }, 37 | singleRun: false, 38 | }); 39 | }; 40 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/src/test.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 11/getting-started-angular-tdd/src/test.ts -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "declaration": false, 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "node", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "useDefineForClassFields": false, 21 | "lib": [ 22 | "ES2022", 23 | "dom" 24 | ] 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter 11/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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": "^16.1.0", 14 | "@angular/common": "^16.1.0", 15 | "@angular/compiler": "^16.1.0", 16 | "@angular/core": "^16.1.0", 17 | "@angular/forms": "^16.1.0", 18 | "@angular/platform-browser": "^16.1.0", 19 | "@angular/platform-browser-dynamic": "^16.1.0", 20 | "@angular/router": "^16.1.0", 21 | "rxjs": "~7.8.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.13.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^16.1.3", 27 | "@angular/cli": "~16.1.3", 28 | "@angular/compiler-cli": "^16.1.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.6.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.2.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.1.0", 36 | "typescript": "~5.1.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes: Routes = [{ path: 'calculator', loadChildren: () => import('./calculator/calculator.module').then(m => m.CalculatorModule) }]; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class AppRoutingModule { } 11 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 2/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |

calculator works!

2 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | describe('CalculatorComponent', () => { 6 | let calculator: CalculatorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CalculatorComponent ] 12 | }) 13 | .compileComponents(); 14 | 15 | fixture = TestBed.createComponent(CalculatorComponent); 16 | calculator = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(calculator).toBeTruthy(); 22 | }); 23 | 24 | it('should add two numbers correctly', () => { 25 | const result = calculator.add(2, 3); 26 | expect(result).toBe(5); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-calculator', 5 | templateUrl: './calculator.component.html', 6 | styleUrls: ['./calculator.component.scss'], 7 | }) 8 | export class CalculatorComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | 7 | 8 | @NgModule({ 9 | declarations: [ 10 | CalculatorComponent 11 | ], 12 | imports: [ 13 | CommonModule, 14 | CalculatorRoutingModule 15 | ] 16 | }) 17 | export class CalculatorModule { } 18 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 2/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 2/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 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 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Chapter 2/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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": "^16.1.0", 14 | "@angular/common": "^16.1.0", 15 | "@angular/compiler": "^16.1.0", 16 | "@angular/core": "^16.1.0", 17 | "@angular/forms": "^16.1.0", 18 | "@angular/platform-browser": "^16.1.0", 19 | "@angular/platform-browser-dynamic": "^16.1.0", 20 | "@angular/router": "^16.1.0", 21 | "rxjs": "~7.8.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.13.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^16.1.3", 27 | "@angular/cli": "~16.1.3", 28 | "@angular/compiler-cli": "^16.1.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.6.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.2.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.1.0", 36 | "typescript": "~5.1.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/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 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class AppRoutingModule { } 11 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 3/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |

{{ result }}

2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorComponent } from './calculator.component'; 4 | import { CalculatorService } from 'src/core/services/calculator.service'; 5 | import { of } from 'rxjs'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | 8 | describe('CalculatorComponent', () => { 9 | let calculator: CalculatorComponent; 10 | let fixture: ComponentFixture; 11 | let calculatorService: CalculatorService; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [CalculatorComponent, ColorChangeDirective], 16 | providers: [CalculatorService] 17 | }).compileComponents(); 18 | 19 | fixture = TestBed.createComponent(CalculatorComponent); 20 | calculator = fixture.componentInstance; 21 | calculatorService = TestBed.inject(CalculatorService); 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(calculator).toBeTruthy(); 27 | }); 28 | 29 | // it('should subscribe to result changes from the calculator service', () => { 30 | // const mockResult = 10; 31 | // // spyOn(calculatorService.result$, 'subscribe').and.returnValue(of(mockResult)); 32 | 33 | 34 | // calculator.ngOnInit(); 35 | 36 | // expect(calculator.result).toEqual(mockResult); 37 | // expect(calculatorService.result$.subscribe).toHaveBeenCalled(); 38 | // }); 39 | 40 | it('should initialize result to 0', () => { 41 | calculator.ngOnInit(); 42 | expect(calculator.result).toEqual(0); 43 | }); 44 | 45 | it('should add two numbers correctly', () => { 46 | spyOn(calculatorService, 'add').and.callThrough(); 47 | calculator.add(2, 3); 48 | expect(calculatorService.add).toHaveBeenCalledWith(2, 3); 49 | expect(calculator.result).toBe(5); 50 | }); 51 | 52 | it('should substract two numbers correctly', () => { 53 | spyOn(calculatorService, 'substract').and.callThrough(); 54 | calculator.substract(2, 3); 55 | expect(calculatorService.substract).toHaveBeenCalledWith(2, 3); 56 | expect(calculator.result).toBe(-1); 57 | }); 58 | 59 | it('should multiply two numbers correctly', () => { 60 | spyOn(calculatorService, 'multiply').and.callThrough(); 61 | calculator.multiply(2, 3); 62 | expect(calculatorService.multiply).toHaveBeenCalledWith(2, 3); 63 | expect(calculator.result).toBe(6); 64 | }); 65 | 66 | it('should divide two numbers correctly', () => { 67 | spyOn(calculatorService, 'divide').and.callThrough(); 68 | calculator.divide(4, 2); 69 | expect(calculatorService.divide).toHaveBeenCalledWith(4, 2); 70 | expect(calculator.result).toBe(2); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CalculatorService } from 'src/core/services/calculator.service'; 3 | 4 | @Component({ 5 | selector: 'app-calculator', 6 | templateUrl: './calculator.component.html', 7 | styleUrls: ['./calculator.component.scss'], 8 | }) 9 | export class CalculatorComponent implements OnInit { 10 | result!: number; 11 | color = 'red'; 12 | 13 | constructor(private calculatorService: CalculatorService) {} 14 | 15 | ngOnInit(): void { 16 | this.result = 0; 17 | } 18 | 19 | add(a: number, b: number): void { 20 | this.result = this.calculatorService.add(a, b); 21 | } 22 | 23 | substract(a: number, b: number): void { 24 | this.result = this.calculatorService.substract(a, b); 25 | } 26 | 27 | multiply(a: number, b: number): void { 28 | this.result = this.calculatorService.multiply(a, b); 29 | } 30 | 31 | divide(a: number, b: number): void { 32 | this.result = this.calculatorService.divide(a, b); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | 8 | 9 | @NgModule({ 10 | declarations: [ 11 | CalculatorComponent, 12 | ColorChangeDirective 13 | ], 14 | imports: [ 15 | CommonModule, 16 | CalculatorRoutingModule 17 | ] 18 | }) 19 | export class CalculatorModule { } 20 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 3/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | 6 | describe('ColorChangeDirective', () => { 7 | let fixture: ComponentFixture; 8 | let calculator: CalculatorComponent; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [ColorChangeDirective, CalculatorComponent], 13 | }).compileComponents(); 14 | 15 | fixture = TestBed.createComponent(CalculatorComponent); 16 | calculator = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should apply the specified color', () => { 21 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 22 | const color: string = 'red'; 23 | calculator.color = color; 24 | fixture.detectChanges(); 25 | 26 | expect(element.style.color).toBe(color); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() { } 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number { 26 | return a / b; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 3/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 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 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Chapter 3/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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": "^16.1.0", 14 | "@angular/common": "^16.1.0", 15 | "@angular/compiler": "^16.1.0", 16 | "@angular/core": "^16.1.0", 17 | "@angular/forms": "^16.1.0", 18 | "@angular/platform-browser": "^16.1.0", 19 | "@angular/platform-browser-dynamic": "^16.1.0", 20 | "@angular/router": "^16.1.0", 21 | "rxjs": "~7.8.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.13.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^16.1.3", 27 | "@angular/cli": "~16.1.3", 28 | "@angular/compiler-cli": "^16.1.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.6.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.2.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.1.0", 36 | "typescript": "~5.1.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/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 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class AppRoutingModule { } 11 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 4/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |

{{ result }}

2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 4/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CalculatorService } from 'src/core/services/calculator.service'; 3 | 4 | @Component({ 5 | selector: 'app-calculator', 6 | templateUrl: './calculator.component.html', 7 | styleUrls: ['./calculator.component.scss'], 8 | }) 9 | export class CalculatorComponent implements OnInit { 10 | result!: number | string; 11 | color = 'red'; 12 | 13 | constructor(private calculatorService: CalculatorService) {} 14 | 15 | ngOnInit(): void { 16 | this.result = 0; 17 | } 18 | 19 | add(a: number, b: number): void { 20 | this.result = this.calculatorService.add(a, b); 21 | } 22 | 23 | substract(a: number, b: number): void { 24 | this.result = this.calculatorService.substract(a, b); 25 | } 26 | 27 | multiply(a: number, b: number): void { 28 | this.result = this.calculatorService.multiply(a, b); 29 | } 30 | 31 | divide(a: number, b: number): void { 32 | this.result = this.calculatorService.divide(a, b); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | 8 | 9 | @NgModule({ 10 | declarations: [ 11 | CalculatorComponent, 12 | ColorChangeDirective 13 | ], 14 | imports: [ 15 | CommonModule, 16 | CalculatorRoutingModule 17 | ] 18 | }) 19 | export class CalculatorModule { } 20 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 4/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | 6 | describe('ColorChangeDirective', () => { 7 | let fixture: ComponentFixture; 8 | let calculator: CalculatorComponent; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [ColorChangeDirective, CalculatorComponent], 13 | }).compileComponents(); 14 | 15 | fixture = TestBed.createComponent(CalculatorComponent); 16 | calculator = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should apply the specified color', () => { 21 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 22 | const color: string = 'red'; 23 | calculator.color = color; 24 | fixture.detectChanges(); 25 | 26 | expect(element.style.color).toBe(color); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/mocks/mock-square-root.service.mock.ts: -------------------------------------------------------------------------------- 1 | export class MockSquareRootService { 2 | calculateSquareRoot(value: number): number { 3 | // Perform a predefined square root calculation based on the input value 4 | if (value === 4) { 5 | return 2; 6 | } else if (value === 9) { 7 | return 3; 8 | } else if (value === 16) { 9 | return 4; 10 | } else { 11 | throw new Error('Invalid input'); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/services/calculator-async.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, fakeAsync } from '@angular/core/testing'; 2 | 3 | import { CalculatorAsyncService } from './calculator-async.service'; 4 | 5 | describe('CalculatorAsyncService', () => { 6 | let service: CalculatorAsyncService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorAsyncService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | 17 | it('should add two numbers', fakeAsync(() => { 18 | let result = 0; 19 | service.add(1, 2).subscribe((val: number) => { 20 | result = val; 21 | }); 22 | 23 | expect(result).toBe(3); 24 | })); 25 | 26 | it('should subtract two numbers', fakeAsync(() => { 27 | let result = 0; 28 | service.subtract(5, 3).subscribe((val: number) => { 29 | result = val; 30 | }); 31 | 32 | expect(result).toBe(2); 33 | })); 34 | 35 | it('should multiply two numbers', fakeAsync(() => { 36 | let result = 0; 37 | service.multiply(3, 4).subscribe((val: number) => { 38 | result = val; 39 | }); 40 | 41 | expect(result).toBe(12); 42 | })); 43 | 44 | it('should divide two numbers', fakeAsync(() => { 45 | let result = 0; 46 | service.divide(10, 2).subscribe((val: number) => { 47 | result = val; 48 | }); 49 | 50 | expect(result).toBe(5); 51 | })); 52 | 53 | it('should throw an error when dividing by zero', fakeAsync(() => { 54 | let error = { message: '' }; 55 | service.divide(10, 0).subscribe({ 56 | error: (err) => (error = err), 57 | }); 58 | expect(error).toBeTruthy(); 59 | expect(error.message).toBe('Cannot divide by zero'); 60 | })); 61 | }); 62 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/services/calculator-async.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, catchError, delay, of, throwError } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root', 6 | }) 7 | export class CalculatorAsyncService { 8 | constructor() {} 9 | 10 | add(a: number, b: number): Observable { 11 | return of(a + b); 12 | } 13 | 14 | subtract(a: number, b: number): Observable { 15 | return of(a - b); 16 | } 17 | 18 | multiply(a: number, b: number): Observable { 19 | return of(a * b); 20 | } 21 | 22 | divide(a: number, b: number): Observable { 23 | if (b === 0) { 24 | return throwError(() => new Error('Cannot divide by zero')); 25 | } 26 | return of(a / b).pipe( 27 | catchError((error) => { 28 | return throwError(() => error); 29 | }) 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root', 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() {} 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number | string { 26 | if (b === 0) { 27 | return 'Division by zero'; 28 | } 29 | 30 | return a / b; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/core/stubs/calculator.service.stub.ts: -------------------------------------------------------------------------------- 1 | export class CalculatorServiceStub { 2 | add(a: number, b: number): number { 3 | return a + b; 4 | } 5 | 6 | substract(a: number, b: number): number { 7 | return a - b; 8 | } 9 | 10 | multiply(a: number, b: number): number { 11 | return a * b; 12 | } 13 | 14 | divide(a: number, b: number): number | Error { 15 | if (b === 0) { 16 | throw new Error('Cannot divide by zero'); 17 | } 18 | return a / b; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 4/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 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 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Chapter 4/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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": "^16.1.0", 14 | "@angular/common": "^16.1.0", 15 | "@angular/compiler": "^16.1.0", 16 | "@angular/core": "^16.1.0", 17 | "@angular/forms": "^16.1.0", 18 | "@angular/platform-browser": "^16.1.0", 19 | "@angular/platform-browser-dynamic": "^16.1.0", 20 | "@angular/router": "^16.1.0", 21 | "rxjs": "~7.8.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.13.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^16.1.3", 27 | "@angular/cli": "~16.1.3", 28 | "@angular/compiler-cli": "^16.1.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.6.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.2.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.1.0", 36 | "typescript": "~5.1.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/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 | { 6 | path: '', 7 | loadChildren: () => 8 | import('./calculator/calculator.module').then((m) => m.CalculatorModule), 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forRoot(routes)], 14 | exports: [RouterModule], 15 | }) 16 | export class AppRoutingModule {} 17 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 5/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | // const fixture = TestBed.createComponent(AppComponent); 19 | // const app = fixture.componentInstance; 20 | // expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | // }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 10 | 13 |

{{ result }}

14 |
15 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 5/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { CalculatorService } from 'src/core/services/calculator.service'; 4 | 5 | @Component({ 6 | selector: 'app-calculator', 7 | templateUrl: './calculator.component.html', 8 | styleUrls: ['./calculator.component.scss'], 9 | }) 10 | export class CalculatorComponent implements OnInit { 11 | result!: number; 12 | color = 'red'; 13 | calculatorForm!: FormGroup; 14 | 15 | constructor(private calculatorService: CalculatorService) { 16 | this.calculatorForm = new FormGroup({ 17 | operand1: new FormControl(null, [Validators.required]), 18 | operand2: new FormControl(null, [Validators.required]), 19 | operator: new FormControl(null, [Validators.required]), 20 | }); 21 | } 22 | 23 | ngOnInit(): void { 24 | this.result = 0; 25 | } 26 | 27 | add(a: number, b: number): void { 28 | this.result = this.calculatorService.add(a, b); 29 | } 30 | 31 | substract(a: number, b: number): void { 32 | this.result = this.calculatorService.substract(a, b); 33 | } 34 | 35 | multiply(a: number, b: number): void { 36 | this.result = this.calculatorService.multiply(a, b); 37 | } 38 | 39 | divide(a: number, b: number): void { 40 | this.result = this.calculatorService.divide(a, b); 41 | } 42 | 43 | calculate(): void { 44 | if (this.calculatorForm.get('operator')?.value === '+') { 45 | this.add( 46 | this.calculatorForm.get('operand1')?.value, 47 | this.calculatorForm.get('operand2')?.value 48 | ); 49 | } 50 | 51 | if (this.calculatorForm.get('operator')?.value === '-') { 52 | this.substract( 53 | this.calculatorForm.get('operand1')?.value, 54 | this.calculatorForm.get('operand2')?.value 55 | ); 56 | } 57 | 58 | if (this.calculatorForm.get('operator')?.value === '*') { 59 | this.multiply( 60 | this.calculatorForm.get('operand1')?.value, 61 | this.calculatorForm.get('operand2')?.value 62 | ); 63 | } 64 | 65 | if (this.calculatorForm.get('operator')?.value === '/') { 66 | this.divide( 67 | this.calculatorForm.get('operand1')?.value, 68 | this.calculatorForm.get('operand2')?.value 69 | ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | import { PercentPipe } from 'src/core/pipes/percent.pipe'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | 10 | 11 | @NgModule({ 12 | declarations: [ 13 | CalculatorComponent, 14 | ColorChangeDirective, 15 | PercentPipe 16 | ], 17 | imports: [ 18 | CommonModule, 19 | ReactiveFormsModule, 20 | CalculatorRoutingModule 21 | ] 22 | }) 23 | export class CalculatorModule { } 24 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 5/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | 7 | describe('ColorChangeDirective', () => { 8 | let fixture: ComponentFixture; 9 | let calculator: CalculatorComponent; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [ColorChangeDirective, CalculatorComponent], 14 | imports: [ReactiveFormsModule] 15 | }).compileComponents(); 16 | 17 | fixture = TestBed.createComponent(CalculatorComponent); 18 | calculator = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should apply the specified color', () => { 23 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 24 | const color: string = 'red'; 25 | calculator.color = color; 26 | fixture.detectChanges(); 27 | 28 | expect(element.style.color).toBe(color); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/core/pipes/percent.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { PercentPipe } from './percent.pipe'; 2 | 3 | describe('PercentPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new PercentPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | 9 | it('should format a positive number to a percentage string', () => { 10 | const input = 123; 11 | const output = new PercentPipe().transform(input); 12 | expect(output).toBe('12300%'); 13 | }); 14 | 15 | it('should format a negative number to a percentage string', () => { 16 | const input = -123; 17 | const output = new PercentPipe().transform(input); 18 | expect(output).toBe('-12300%'); 19 | }); 20 | 21 | it('should format a decimal number to a percentage string', () => { 22 | const input = 123.45; 23 | const output = new PercentPipe().transform(input); 24 | expect(output).toBe('12345%'); 25 | }); 26 | 27 | it('should format a zero number to a percentage string', () => { 28 | const input = 0; 29 | const output = new PercentPipe().transform(input); 30 | expect(output).toBe('0%'); 31 | }); 32 | 33 | it('should return an Error when the value is not a number NaN', () => { 34 | const input = NaN; 35 | const output = new PercentPipe().transform(input); 36 | expect(output).toBe('Error'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/core/pipes/percent.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'percent', 5 | }) 6 | export class PercentPipe implements PipeTransform { 7 | 8 | transform(value: number): string { 9 | if (isNaN(value)) { 10 | return 'Error'; 11 | } 12 | 13 | const formattedValue = value * 100; 14 | return formattedValue + '%'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() { } 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number { 26 | return a / b; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 5/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 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 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Chapter 5/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('template spec', () => { 2 | it('passes', () => { 3 | cy.visit('https://example.cypress.io') 4 | }) 5 | }) -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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 | "cypress:open": "cypress open" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^16.1.0", 15 | "@angular/common": "^16.1.0", 16 | "@angular/compiler": "^16.1.0", 17 | "@angular/core": "^16.1.0", 18 | "@angular/forms": "^16.1.0", 19 | "@angular/platform-browser": "^16.1.0", 20 | "@angular/platform-browser-dynamic": "^16.1.0", 21 | "@angular/router": "^16.1.0", 22 | "rxjs": "~7.8.0", 23 | "tslib": "^2.3.0", 24 | "zone.js": "~0.13.0" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "^16.1.3", 28 | "@angular/cli": "~16.1.3", 29 | "@angular/compiler-cli": "^16.1.0", 30 | "@types/jasmine": "~4.3.0", 31 | "cypress": "^13.6.2", 32 | "jasmine-core": "~4.6.0", 33 | "karma": "~6.4.0", 34 | "karma-chrome-launcher": "~3.2.0", 35 | "karma-coverage": "~2.2.0", 36 | "karma-jasmine": "~5.1.0", 37 | "karma-jasmine-html-reporter": "~2.1.0", 38 | "typescript": "~5.1.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/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 | { 6 | path: '', 7 | loadChildren: () => 8 | import('./calculator/calculator.module').then((m) => m.CalculatorModule), 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forRoot(routes)], 14 | exports: [RouterModule], 15 | }) 16 | export class AppRoutingModule {} 17 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 7/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | // const fixture = TestBed.createComponent(AppComponent); 19 | // const app = fixture.componentInstance; 20 | // expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | // }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 10 | 13 |

{{ result }}

14 |
15 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 7/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { CalculatorService } from 'src/core/services/calculator.service'; 4 | 5 | @Component({ 6 | selector: 'app-calculator', 7 | templateUrl: './calculator.component.html', 8 | styleUrls: ['./calculator.component.scss'], 9 | }) 10 | export class CalculatorComponent implements OnInit { 11 | result!: number; 12 | color = 'red'; 13 | calculatorForm!: FormGroup; 14 | 15 | constructor(private calculatorService: CalculatorService) { 16 | this.calculatorForm = new FormGroup({ 17 | operand1: new FormControl(null, [Validators.required]), 18 | operand2: new FormControl(null, [Validators.required]), 19 | operator: new FormControl(null, [Validators.required]), 20 | }); 21 | } 22 | 23 | ngOnInit(): void { 24 | this.result = 0; 25 | } 26 | 27 | add(a: number, b: number): void { 28 | this.result = this.calculatorService.add(a, b); 29 | } 30 | 31 | substract(a: number, b: number): void { 32 | this.result = this.calculatorService.substract(a, b); 33 | } 34 | 35 | multiply(a: number, b: number): void { 36 | this.result = this.calculatorService.multiply(a, b); 37 | } 38 | 39 | divide(a: number, b: number): void { 40 | this.result = this.calculatorService.divide(a, b); 41 | } 42 | 43 | calculate(): void { 44 | if (this.calculatorForm.get('operator')?.value === '+') { 45 | this.add( 46 | this.calculatorForm.get('operand1')?.value, 47 | this.calculatorForm.get('operand2')?.value 48 | ); 49 | } 50 | 51 | if (this.calculatorForm.get('operator')?.value === '-') { 52 | this.substract( 53 | this.calculatorForm.get('operand1')?.value, 54 | this.calculatorForm.get('operand2')?.value 55 | ); 56 | } 57 | 58 | if (this.calculatorForm.get('operator')?.value === '*') { 59 | this.multiply( 60 | this.calculatorForm.get('operand1')?.value, 61 | this.calculatorForm.get('operand2')?.value 62 | ); 63 | } 64 | 65 | if (this.calculatorForm.get('operator')?.value === '/') { 66 | this.divide( 67 | this.calculatorForm.get('operand1')?.value, 68 | this.calculatorForm.get('operand2')?.value 69 | ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | import { PercentPipe } from 'src/core/pipes/percent.pipe'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | 10 | 11 | @NgModule({ 12 | declarations: [ 13 | CalculatorComponent, 14 | ColorChangeDirective, 15 | PercentPipe 16 | ], 17 | imports: [ 18 | CommonModule, 19 | ReactiveFormsModule, 20 | CalculatorRoutingModule 21 | ] 22 | }) 23 | export class CalculatorModule { } 24 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 7/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | 7 | describe('ColorChangeDirective', () => { 8 | let fixture: ComponentFixture; 9 | let calculator: CalculatorComponent; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [ColorChangeDirective, CalculatorComponent], 14 | imports: [ReactiveFormsModule] 15 | }).compileComponents(); 16 | 17 | fixture = TestBed.createComponent(CalculatorComponent); 18 | calculator = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should apply the specified color', () => { 23 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 24 | const color: string = 'red'; 25 | calculator.color = color; 26 | fixture.detectChanges(); 27 | 28 | expect(element.style.color).toBe(color); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/core/pipes/percent.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { PercentPipe } from './percent.pipe'; 2 | 3 | describe('PercentPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new PercentPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | 9 | it('should format a positive number to a percentage string', () => { 10 | const input = 123; 11 | const output = new PercentPipe().transform(input); 12 | expect(output).toBe('12300%'); 13 | }); 14 | 15 | it('should format a negative number to a percentage string', () => { 16 | const input = -123; 17 | const output = new PercentPipe().transform(input); 18 | expect(output).toBe('-12300%'); 19 | }); 20 | 21 | it('should format a decimal number to a percentage string', () => { 22 | const input = 123.45; 23 | const output = new PercentPipe().transform(input); 24 | expect(output).toBe('12345%'); 25 | }); 26 | 27 | it('should format a zero number to a percentage string', () => { 28 | const input = 0; 29 | const output = new PercentPipe().transform(input); 30 | expect(output).toBe('0%'); 31 | }); 32 | 33 | it('should format a zero number to a percentage string', () => { 34 | const input = NaN; 35 | const output = new PercentPipe().transform(input); 36 | expect(output).toBe('Error'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/core/pipes/percent.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'percent', 5 | }) 6 | export class PercentPipe implements PipeTransform { 7 | 8 | transform(value: number): string { 9 | if (isNaN(value)) { 10 | return 'Error'; 11 | } 12 | 13 | const formattedValue = value * 100; 14 | return formattedValue + '%'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() { } 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number { 26 | return a / b; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 7/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "declaration": false, 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "node", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "useDefineForClassFields": false, 21 | "lib": [ 22 | "ES2022", 23 | "dom" 24 | ] 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter 7/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('template spec', () => { 2 | it('passes', () => { 3 | cy.visit('https://example.cypress.io') 4 | }) 5 | }) -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } 38 | 39 | Cypress.Commands.add( 40 | 'performCalculation', 41 | 42 | (firstNumber, operator, secondNumber) => { 43 | cy.visit('http://localhost:4200/'); 44 | cy.get('input').first().type(firstNumber); 45 | cy.get('select').select(operator).should('have.value', operator); 46 | cy.get('input').last().type(secondNumber); 47 | cy.get('button').click(); 48 | } 49 | ); 50 | 51 | declare namespace Cypress { 52 | interface Chainable { 53 | performCalculation( 54 | firstNumber: string, 55 | operator: string, 56 | secondNumber: string 57 | ): Chainable; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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 | "cypress:open": "cypress open" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^16.1.0", 15 | "@angular/common": "^16.1.0", 16 | "@angular/compiler": "^16.1.0", 17 | "@angular/core": "^16.1.0", 18 | "@angular/forms": "^16.1.0", 19 | "@angular/platform-browser": "^16.1.0", 20 | "@angular/platform-browser-dynamic": "^16.1.0", 21 | "@angular/router": "^16.1.0", 22 | "rxjs": "~7.8.0", 23 | "tslib": "^2.3.0", 24 | "zone.js": "~0.13.0" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "^16.1.3", 28 | "@angular/cli": "~16.1.3", 29 | "@angular/compiler-cli": "^16.1.0", 30 | "@types/jasmine": "~4.3.0", 31 | "cypress": "^13.6.2", 32 | "jasmine-core": "~4.6.0", 33 | "karma": "~6.4.0", 34 | "karma-chrome-launcher": "~3.2.0", 35 | "karma-coverage": "~2.2.0", 36 | "karma-jasmine": "~5.1.0", 37 | "karma-jasmine-html-reporter": "~2.1.0", 38 | "typescript": "~5.1.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/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 | { 6 | path: '', 7 | loadChildren: () => 8 | import('./calculator/calculator.module').then((m) => m.CalculatorModule), 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forRoot(routes)], 14 | exports: [RouterModule], 15 | }) 16 | export class AppRoutingModule {} 17 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 8/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | // const fixture = TestBed.createComponent(AppComponent); 19 | // const app = fixture.componentInstance; 20 | // expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | // }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 10 | 13 |

{{ result }}

14 |
15 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 8/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { CalculatorService } from 'src/core/services/calculator.service'; 4 | 5 | @Component({ 6 | selector: 'app-calculator', 7 | templateUrl: './calculator.component.html', 8 | styleUrls: ['./calculator.component.scss'], 9 | }) 10 | export class CalculatorComponent implements OnInit { 11 | result!: number; 12 | color = 'red'; 13 | calculatorForm!: FormGroup; 14 | 15 | constructor(private calculatorService: CalculatorService) { 16 | this.calculatorForm = new FormGroup({ 17 | operand1: new FormControl(null, [Validators.required]), 18 | operand2: new FormControl(null, [Validators.required]), 19 | operator: new FormControl(null, [Validators.required]), 20 | }); 21 | } 22 | 23 | ngOnInit(): void { 24 | this.result = 0; 25 | } 26 | 27 | add(a: number, b: number): void { 28 | this.result = this.calculatorService.add(a, b); 29 | } 30 | 31 | substract(a: number, b: number): void { 32 | this.result = this.calculatorService.substract(a, b); 33 | } 34 | 35 | multiply(a: number, b: number): void { 36 | this.result = this.calculatorService.multiply(a, b); 37 | } 38 | 39 | divide(a: number, b: number): void { 40 | this.result = this.calculatorService.divide(a, b); 41 | } 42 | 43 | calculate(): void { 44 | if (this.calculatorForm.get('operator')?.value === '+') { 45 | this.add( 46 | this.calculatorForm.get('operand1')?.value, 47 | this.calculatorForm.get('operand2')?.value 48 | ); 49 | } 50 | 51 | if (this.calculatorForm.get('operator')?.value === '-') { 52 | this.substract( 53 | this.calculatorForm.get('operand1')?.value, 54 | this.calculatorForm.get('operand2')?.value 55 | ); 56 | } 57 | 58 | if (this.calculatorForm.get('operator')?.value === '*') { 59 | this.multiply( 60 | this.calculatorForm.get('operand1')?.value, 61 | this.calculatorForm.get('operand2')?.value 62 | ); 63 | } 64 | 65 | if (this.calculatorForm.get('operator')?.value === '/') { 66 | this.divide( 67 | this.calculatorForm.get('operand1')?.value, 68 | this.calculatorForm.get('operand2')?.value 69 | ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | import { PercentPipe } from 'src/core/pipes/percent.pipe'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | 10 | 11 | @NgModule({ 12 | declarations: [ 13 | CalculatorComponent, 14 | ColorChangeDirective, 15 | PercentPipe 16 | ], 17 | imports: [ 18 | CommonModule, 19 | ReactiveFormsModule, 20 | CalculatorRoutingModule 21 | ] 22 | }) 23 | export class CalculatorModule { } 24 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 8/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | 7 | describe('ColorChangeDirective', () => { 8 | let fixture: ComponentFixture; 9 | let calculator: CalculatorComponent; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [ColorChangeDirective, CalculatorComponent], 14 | imports: [ReactiveFormsModule] 15 | }).compileComponents(); 16 | 17 | fixture = TestBed.createComponent(CalculatorComponent); 18 | calculator = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should apply the specified color', () => { 23 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 24 | const color: string = 'red'; 25 | calculator.color = color; 26 | fixture.detectChanges(); 27 | 28 | expect(element.style.color).toBe(color); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/core/pipes/percent.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { PercentPipe } from './percent.pipe'; 2 | 3 | describe('PercentPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new PercentPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | 9 | it('should format a positive number to a percentage string', () => { 10 | const input = 123; 11 | const output = new PercentPipe().transform(input); 12 | expect(output).toBe('12300%'); 13 | }); 14 | 15 | it('should format a negative number to a percentage string', () => { 16 | const input = -123; 17 | const output = new PercentPipe().transform(input); 18 | expect(output).toBe('-12300%'); 19 | }); 20 | 21 | it('should format a decimal number to a percentage string', () => { 22 | const input = 123.45; 23 | const output = new PercentPipe().transform(input); 24 | expect(output).toBe('12345%'); 25 | }); 26 | 27 | it('should format a zero number to a percentage string', () => { 28 | const input = 0; 29 | const output = new PercentPipe().transform(input); 30 | expect(output).toBe('0%'); 31 | }); 32 | 33 | it('should format a zero number to a percentage string', () => { 34 | const input = NaN; 35 | const output = new PercentPipe().transform(input); 36 | expect(output).toBe('Error'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/core/pipes/percent.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'percent', 5 | }) 6 | export class PercentPipe implements PipeTransform { 7 | 8 | transform(value: number): string { 9 | if (isNaN(value)) { 10 | return 'Error'; 11 | } 12 | 13 | const formattedValue = value * 100; 14 | return formattedValue + '%'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() { } 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number { 26 | return a / b; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 8/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "declaration": false, 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "node", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "useDefineForClassFields": false, 21 | "lib": [ 22 | "ES2022", 23 | "dom" 24 | ] 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter 8/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/.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 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/README.md: -------------------------------------------------------------------------------- 1 | # GettingStartedAngularTdd 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | 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. 28 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('template spec', () => { 2 | it('passes', () => { 3 | cy.visit('https://example.cypress.io') 4 | }) 5 | }) -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } 38 | 39 | Cypress.Commands.add( 40 | 'performCalculation', 41 | 42 | (firstNumber, operator, secondNumber) => { 43 | cy.get('input').first().type(firstNumber); 44 | cy.get('select').select(operator).should('have.value', operator); 45 | cy.get('input').last().type(secondNumber); 46 | cy.get('button').click(); 47 | } 48 | ); 49 | 50 | declare namespace Cypress { 51 | interface Chainable { 52 | performCalculation( 53 | firstNumber: string, 54 | operator: string, 55 | secondNumber: string 56 | ): Chainable; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-angular-tdd", 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 | "cypress:open": "cypress open" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^16.1.0", 15 | "@angular/common": "^16.1.0", 16 | "@angular/compiler": "^16.1.0", 17 | "@angular/core": "^16.1.0", 18 | "@angular/forms": "^16.1.0", 19 | "@angular/platform-browser": "^16.1.0", 20 | "@angular/platform-browser-dynamic": "^16.1.0", 21 | "@angular/router": "^16.1.0", 22 | "rxjs": "~7.8.0", 23 | "tslib": "^2.3.0", 24 | "zone.js": "~0.13.0" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "^16.1.3", 28 | "@angular/cli": "~16.1.3", 29 | "@angular/compiler-cli": "^16.1.0", 30 | "@types/jasmine": "~4.3.0", 31 | "cypress": "^13.6.2", 32 | "jasmine-core": "~4.6.0", 33 | "karma": "~6.4.0", 34 | "karma-chrome-launcher": "~3.2.0", 35 | "karma-coverage": "~2.2.0", 36 | "karma-coverage-istanbul-reporter": "^3.0.3", 37 | "karma-jasmine": "~5.1.0", 38 | "karma-jasmine-html-reporter": "~2.1.0", 39 | "puppeteer": "^22.3.0", 40 | "typescript": "~5.1.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/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 | { 6 | path: '', 7 | loadChildren: () => 8 | import('./calculator/calculator.module').then((m) => m.CalculatorModule), 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forRoot(routes)], 14 | exports: [RouterModule], 15 | }) 16 | export class AppRoutingModule {} 17 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 9/getting-started-angular-tdd/src/app/app.component.scss -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule], 8 | declarations: [AppComponent] 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 as title 'getting-started-angular-tdd'`, () => { 18 | // const fixture = TestBed.createComponent(AppComponent); 19 | // const app = fixture.componentInstance; 20 | // expect(app.title).toEqual('getting-started-angular-tdd'); 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('.content span')?.textContent).toContain('getting-started-angular-tdd app is running!'); 28 | // }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/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.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'getting-started-angular-tdd'; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/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 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/calculator/calculator-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { CalculatorComponent } from './calculator.component'; 4 | 5 | const routes: Routes = [{ path: '', component: CalculatorComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class CalculatorRoutingModule { } 12 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/calculator/calculator.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 10 | 13 |

{{ result }}

14 |
15 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/calculator/calculator.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 9/getting-started-angular-tdd/src/app/calculator/calculator.component.scss -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { CalculatorService } from 'src/core/services/calculator.service'; 4 | 5 | @Component({ 6 | selector: 'app-calculator', 7 | templateUrl: './calculator.component.html', 8 | styleUrls: ['./calculator.component.scss'], 9 | }) 10 | export class CalculatorComponent implements OnInit { 11 | result!: number; 12 | color = 'red'; 13 | calculatorForm!: FormGroup; 14 | 15 | constructor(private calculatorService: CalculatorService) { 16 | this.calculatorForm = new FormGroup({ 17 | operand1: new FormControl(null, [Validators.required]), 18 | operand2: new FormControl(null, [Validators.required]), 19 | operator: new FormControl(null, [Validators.required]), 20 | }); 21 | } 22 | 23 | ngOnInit(): void { 24 | this.result = 0; 25 | } 26 | 27 | add(a: number, b: number): void { 28 | this.result = this.calculatorService.add(a, b); 29 | } 30 | 31 | substract(a: number, b: number): void { 32 | this.result = this.calculatorService.substract(a, b); 33 | } 34 | 35 | multiply(a: number, b: number): void { 36 | this.result = this.calculatorService.multiply(a, b); 37 | } 38 | 39 | divide(a: number, b: number): void { 40 | this.result = this.calculatorService.divide(a, b); 41 | } 42 | 43 | calculate(): void { 44 | if (this.calculatorForm.get('operator')?.value === '+') { 45 | this.add( 46 | this.calculatorForm.get('operand1')?.value, 47 | this.calculatorForm.get('operand2')?.value 48 | ); 49 | } 50 | 51 | if (this.calculatorForm.get('operator')?.value === '-') { 52 | this.substract( 53 | this.calculatorForm.get('operand1')?.value, 54 | this.calculatorForm.get('operand2')?.value 55 | ); 56 | } 57 | 58 | if (this.calculatorForm.get('operator')?.value === '*') { 59 | this.multiply( 60 | this.calculatorForm.get('operand1')?.value, 61 | this.calculatorForm.get('operand2')?.value 62 | ); 63 | } 64 | 65 | if (this.calculatorForm.get('operator')?.value === '/') { 66 | this.divide( 67 | this.calculatorForm.get('operand1')?.value, 68 | this.calculatorForm.get('operand2')?.value 69 | ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/app/calculator/calculator.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { CalculatorRoutingModule } from './calculator-routing.module'; 5 | import { CalculatorComponent } from './calculator.component'; 6 | import { ColorChangeDirective } from 'src/core/directives/color-change.directive'; 7 | import { PercentPipe } from 'src/core/pipes/percent.pipe'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | 10 | 11 | @NgModule({ 12 | declarations: [ 13 | CalculatorComponent, 14 | ColorChangeDirective, 15 | PercentPipe 16 | ], 17 | imports: [ 18 | CommonModule, 19 | ReactiveFormsModule, 20 | CalculatorRoutingModule 21 | ] 22 | }) 23 | export class CalculatorModule { } 24 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 9/getting-started-angular-tdd/src/assets/.gitkeep -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/core/directives/color-change.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { By } from '@angular/platform-browser'; 3 | import { ColorChangeDirective } from './color-change.directive'; 4 | import { CalculatorComponent } from 'src/app/calculator/calculator.component'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | 7 | describe('ColorChangeDirective', () => { 8 | let fixture: ComponentFixture; 9 | let calculator: CalculatorComponent; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [ColorChangeDirective, CalculatorComponent], 14 | imports: [ReactiveFormsModule] 15 | }).compileComponents(); 16 | 17 | fixture = TestBed.createComponent(CalculatorComponent); 18 | calculator = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should apply the specified color', () => { 23 | const element: HTMLElement = fixture.debugElement.query(By.css('p')).nativeElement; 24 | const color: string = 'red'; 25 | calculator.color = color; 26 | fixture.detectChanges(); 27 | 28 | expect(element.style.color).toBe(color); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/core/directives/color-change.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[colorChange]', 5 | }) 6 | export class ColorChangeDirective implements OnInit { 7 | @Input() colorChange!: string; 8 | constructor(private elementRef: ElementRef, private renderer: Renderer2) {} 9 | 10 | ngOnInit() { 11 | this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.colorChange); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/core/pipes/percent.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { PercentPipe } from './percent.pipe'; 2 | 3 | describe('PercentPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new PercentPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | 9 | it('should format a positive number to a percentage string', () => { 10 | const input = 123; 11 | const output = new PercentPipe().transform(input); 12 | expect(output).toBe('12300%'); 13 | }); 14 | 15 | it('should format a negative number to a percentage string', () => { 16 | const input = -123; 17 | const output = new PercentPipe().transform(input); 18 | expect(output).toBe('-12300%'); 19 | }); 20 | 21 | it('should format a decimal number to a percentage string', () => { 22 | const input = 123.45; 23 | const output = new PercentPipe().transform(input); 24 | expect(output).toBe('12345%'); 25 | }); 26 | 27 | it('should format a zero number to a percentage string', () => { 28 | const input = 0; 29 | const output = new PercentPipe().transform(input); 30 | expect(output).toBe('0%'); 31 | }); 32 | 33 | it('should format a zero number to a percentage string', () => { 34 | const input = NaN; 35 | const output = new PercentPipe().transform(input); 36 | expect(output).toBe('Error'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/core/pipes/percent.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'percent', 5 | }) 6 | export class PercentPipe implements PipeTransform { 7 | 8 | transform(value: number): string { 9 | if (isNaN(value)) { 10 | return 'Error'; 11 | } 12 | 13 | const formattedValue = value * 100; 14 | return formattedValue + '%'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/core/services/calculator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CalculatorService } from './calculator.service'; 4 | 5 | describe('CalculatorService', () => { 6 | let service: CalculatorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(CalculatorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/core/services/calculator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CalculatorService { 8 | private resultSubject = new BehaviorSubject(0); 9 | public result$ = this.resultSubject.asObservable(); 10 | 11 | constructor() { } 12 | 13 | add(a: number, b: number): number { 14 | return a + b; 15 | } 16 | 17 | substract(a: number, b: number): number { 18 | return a - b; 19 | } 20 | 21 | multiply(a: number, b: number): number { 22 | return a * b; 23 | } 24 | 25 | divide(a: number, b: number): number { 26 | return a / b; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 9/getting-started-angular-tdd/src/favicon.ico -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GettingStartedAngularTdd 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | process.env.CHROME_BIN = require("puppeteer").executablePath(); 5 | 6 | module.exports = function (config) { 7 | config.set({ 8 | basePath: "", 9 | frameworks: ["jasmine", "@angular-devkit/build-angular"], 10 | plugins: [ 11 | require("karma-jasmine"), 12 | require("karma-chrome-launcher"), 13 | require("karma-jasmine-html-reporter"), 14 | require("karma-coverage-istanbul-reporter"), 15 | require("@angular-devkit/build-angular/plugins/karma"), 16 | ], 17 | client: { 18 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 19 | }, 20 | coverageIstanbulReporter: { 21 | dir: require("path").join(__dirname, "../coverage"), 22 | reports: ["html", "lcovonly"], 23 | fixWebpackSourcePaths: true, 24 | }, 25 | reporters: ["progress", "kjhtml"], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ["Chrome"], 31 | customLaunchers: { 32 | ChromeHeadlessCI: { 33 | base: "ChromeHeadless", 34 | flags: ["--no-sandbox", "--disable-gpu"], 35 | }, 36 | }, 37 | singleRun: false, 38 | }); 39 | }; 40 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | 6 | platformBrowserDynamic().bootstrapModule(AppModule) 7 | .catch(err => console.error(err)); 8 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/src/test.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Angular-Test-Driven-Development/6b84c6d75679b4cae4e995e6925cba416736a98f/Chapter 9/getting-started-angular-tdd/src/test.ts -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/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 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/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 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "declaration": false, 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "node", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "useDefineForClassFields": false, 21 | "lib": [ 22 | "ES2022", 23 | "dom" 24 | ] 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter 9/getting-started-angular-tdd/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 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | --------------------------------------------------------------------------------