├── .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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------