From 7bf8b73fb7bd897262efe3f7583ca1c18bdbb0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCller?= Date: Sun, 2 May 2021 15:38:28 +0200 Subject: [PATCH] BETTERZON-83: Writing unit tests for angular to improve code coverage --- Frontend/angular.json | 256 +++++++++--------- Frontend/src/app/app.component.spec.ts | 1 + Frontend/src/app/app.routing.ts | 2 +- .../footer/footer.component.spec.ts | 13 + .../app/components/footer/footer.component.ts | 2 +- .../header/header.component.spec.ts | 10 + .../newest-prices-list.component.spec.ts | 12 + .../product-details.component.spec.ts | 12 +- .../product-list.component.spec.ts | 35 ++- .../landingpage/landingpage.component.spec.ts | 6 + Frontend/tsconfig.spec.json | 28 +- 11 files changed, 232 insertions(+), 145 deletions(-) diff --git a/Frontend/angular.json b/Frontend/angular.json index 9a6515f..6e520a0 100644 --- a/Frontend/angular.json +++ b/Frontend/angular.json @@ -1,133 +1,135 @@ { - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "Betterzon": { - "projectType": "application", - "schematics": {}, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/Betterzon", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.app.json", - "aot": true, - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", - "src/styles.css", - "./node_modules/cookieconsent/build/cookieconsent.min.css" - ], - "scripts": [ - "./node_modules/cookieconsent/build/cookieconsent.min.js" - ] - }, - "configurations": { - "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "extractCss": true, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "budgets": [ - { - "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "Betterzon": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/Betterzon", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "aot": true, + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", + "src/styles.css", + "./node_modules/cookieconsent/build/cookieconsent.min.css" + ], + "scripts": [ + "./node_modules/cookieconsent/build/cookieconsent.min.js" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + } + } }, - { - "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "Betterzon:build" + }, + "configurations": { + "production": { + "browserTarget": "Betterzon:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "Betterzon:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "codeCoverage": true, + "codeCoverageExclude": ["src/app/mocks/mock.service.ts"], + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", + "src/styles.css", + "./node_modules/cookieconsent/build/cookieconsent.min.css" + ], + "scripts": [ + "./node_modules/cookieconsent/build/cookieconsent.min.js" + ] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "tsconfig.app.json", + "tsconfig.spec.json", + "e2e/tsconfig.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + }, + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "Betterzon:serve" + }, + "configurations": { + "production": { + "devServerTarget": "Betterzon:serve:production" + } + } } - ] } - } - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "Betterzon:build" - }, - "configurations": { - "production": { - "browserTarget": "Betterzon:build:production" - } - } - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "Betterzon:build" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.spec.json", - "karmaConfig": "karma.conf.js", - "codeCoverage": true, - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", - "src/styles.css", - "./node_modules/cookieconsent/build/cookieconsent.min.css" - ], - "scripts": [ - "./node_modules/cookieconsent/build/cookieconsent.min.js" - ] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "e2e/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - }, - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "Betterzon:serve" - }, - "configurations": { - "production": { - "devServerTarget": "Betterzon:serve:production" - } - } } - } - }}, - "defaultProject": "Betterzon" + }, + "defaultProject": "Betterzon" } diff --git a/Frontend/src/app/app.component.spec.ts b/Frontend/src/app/app.component.spec.ts index c86b6ad..a309d0b 100644 --- a/Frontend/src/app/app.component.spec.ts +++ b/Frontend/src/app/app.component.spec.ts @@ -28,6 +28,7 @@ describe('AppComponent', () => { it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; + app.ngOnInit(); expect(app).toBeTruthy(); }); diff --git a/Frontend/src/app/app.routing.ts b/Frontend/src/app/app.routing.ts index c38d9ba..1994506 100644 --- a/Frontend/src/app/app.routing.ts +++ b/Frontend/src/app/app.routing.ts @@ -11,7 +11,7 @@ import {ImprintComponent} from './pages/imprint/imprint.component'; import {PrivacyComponent} from './pages/privacy/privacy.component'; const routes: Routes = [ - {path: '', component: LandingpageComponent}, + {path: '', component: LandingpageComponent, pathMatch: 'full'}, {path: 'search', component: ProductSearchPageComponent}, {path: 'product/:id', component: ProductDetailPageComponent}, {path: 'impressum', component: ImprintComponent}, diff --git a/Frontend/src/app/components/footer/footer.component.spec.ts b/Frontend/src/app/components/footer/footer.component.spec.ts index 2d93572..d75b1f9 100644 --- a/Frontend/src/app/components/footer/footer.component.spec.ts +++ b/Frontend/src/app/components/footer/footer.component.spec.ts @@ -2,13 +2,21 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {FooterComponent} from './footer.component'; import {RouterTestingModule} from "@angular/router/testing"; +import {AppComponent} from "../../app.component"; +import {ImprintComponent} from "../../pages/imprint/imprint.component"; +import {ActivatedRoute, Router} from "@angular/router"; describe('FooterComponent', () => { let component: FooterComponent; let fixture: ComponentFixture; + let router = { + navigate: jasmine.createSpy('navigate'), + routerState: jasmine.createSpy('routerState') + } beforeEach(async () => { await TestBed.configureTestingModule({ + providers: [{provide: Router, useValue: router}], declarations: [FooterComponent], imports: [ RouterTestingModule @@ -26,4 +34,9 @@ describe('FooterComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should navigate to /impressum when navigateImprint() is called', () => { + component.navigateImprint(); + expect(router.navigate).toHaveBeenCalledWith(['/impressum']); + }); }); diff --git a/Frontend/src/app/components/footer/footer.component.ts b/Frontend/src/app/components/footer/footer.component.ts index 502c475..f587c32 100644 --- a/Frontend/src/app/components/footer/footer.component.ts +++ b/Frontend/src/app/components/footer/footer.component.ts @@ -17,7 +17,7 @@ export class FooterComponent implements OnInit { } navigateImprint(): void { - this.router.navigate([('/impressum/')]); + this.router.navigate([('/impressum')]); } } diff --git a/Frontend/src/app/components/header/header.component.spec.ts b/Frontend/src/app/components/header/header.component.spec.ts index edae81c..b2ecacd 100644 --- a/Frontend/src/app/components/header/header.component.spec.ts +++ b/Frontend/src/app/components/header/header.component.spec.ts @@ -3,13 +3,23 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HeaderComponent} from './header.component'; import {RouterTestingModule} from "@angular/router/testing"; import {MatMenuModule} from "@angular/material/menu"; +import {Router} from "@angular/router"; describe('HeaderComponent', () => { let component: HeaderComponent; let fixture: ComponentFixture; + let router = { + navigate: jasmine.createSpy('navigate'), + navigateByUrl: (url: string) => { + return { + then: () => {} + } + } + } beforeEach(async () => { await TestBed.configureTestingModule({ + providers: [{provide: Router, useValue: router}], declarations: [HeaderComponent], imports: [ RouterTestingModule, diff --git a/Frontend/src/app/components/newest-prices-list/newest-prices-list.component.spec.ts b/Frontend/src/app/components/newest-prices-list/newest-prices-list.component.spec.ts index 0150f0a..959cbb3 100644 --- a/Frontend/src/app/components/newest-prices-list/newest-prices-list.component.spec.ts +++ b/Frontend/src/app/components/newest-prices-list/newest-prices-list.component.spec.ts @@ -8,10 +8,22 @@ import {ApiService} from "../../services/api.service"; class MockApiService extends AbstractMockObservableService { getCurrentPricePerVendor() { + this.content = []; return this; } getVendors() { + const vendor = { + vendor_id: 1, + name: 'Max Mustermann', + streetname: 'Musterstraße 69', + zip_code: '12345', + city: 'Musterhausen', + country_code: 'DE', + phone: '+49 123 4567890', + website: 'https://www.amazon.de', + }; + this.content = [vendor]; return this; } } diff --git a/Frontend/src/app/components/product-details/product-details.component.spec.ts b/Frontend/src/app/components/product-details/product-details.component.spec.ts index c1fbd09..15b2fa4 100644 --- a/Frontend/src/app/components/product-details/product-details.component.spec.ts +++ b/Frontend/src/app/components/product-details/product-details.component.spec.ts @@ -30,7 +30,17 @@ class MockApiService extends AbstractMockObservableService { } getVendors() { - this.content = []; + const vendor = { + vendor_id: 1, + name: 'Max Mustermann', + streetname: 'Musterstraße 69', + zip_code: '12345', + city: 'Musterhausen', + country_code: 'DE', + phone: '+49 123 4567890', + website: 'https://www.amazon.de', + } + this.content = [vendor]; return this; } } diff --git a/Frontend/src/app/components/product-list/product-list.component.spec.ts b/Frontend/src/app/components/product-list/product-list.component.spec.ts index 83e7ebd..b0c733f 100644 --- a/Frontend/src/app/components/product-list/product-list.component.spec.ts +++ b/Frontend/src/app/components/product-list/product-list.component.spec.ts @@ -6,13 +6,16 @@ import {HeaderComponent} from "../header/header.component"; import {RouterTestingModule} from "@angular/router/testing"; import {ApiService} from "../../services/api.service"; import {AbstractMockObservableService} from "../../mocks/mock.service"; +import {Router} from "@angular/router"; class MockApiService extends AbstractMockObservableService { getProducts() { + this.content = []; return this; } getProductsByQuery() { + this.content = []; return this; } } @@ -21,11 +24,15 @@ describe('ProductListComponent', () => { let component: ProductListComponent; let fixture: ComponentFixture; let mockService; + let router = { + navigate: jasmine.createSpy('navigate'), + routerState: jasmine.createSpy('routerState') + } beforeEach(async () => { mockService = new MockApiService(); await TestBed.configureTestingModule({ - providers: [{provide: ApiService, useValue: mockService}], + providers: [{provide: ApiService, useValue: mockService}, {provide: Router, useValue: router}], declarations: [ProductListComponent], imports: [ RouterTestingModule @@ -43,4 +50,30 @@ describe('ProductListComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should load products by search query when type is search', () => { + component.type = 'search'; + component.loadParams(); + expect(component.products).toBeTruthy(); + }); + + it('should navigate to /product/xyz when navigateImprint() is called', () => { + const product = { + product_id: 1, + asin: 'ASIN', + is_active: true, + name: 'Super tolles Produkt', + short_description: 'Descr', + long_description: 'Descr', + image_guid: '123', + date_added: new Date(), + last_modified: new Date(), + manufacturer_id: 1, + selling_rank: '1', + category_id: 1 + } + + component.clickedProduct(product); + expect(router.navigate).toHaveBeenCalledWith(['/product/1']); + }); }); diff --git a/Frontend/src/app/pages/landingpage/landingpage.component.spec.ts b/Frontend/src/app/pages/landingpage/landingpage.component.spec.ts index 7218360..92bdbb7 100644 --- a/Frontend/src/app/pages/landingpage/landingpage.component.spec.ts +++ b/Frontend/src/app/pages/landingpage/landingpage.component.spec.ts @@ -2,13 +2,19 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {LandingpageComponent} from './landingpage.component'; import {RouterTestingModule} from "@angular/router/testing"; +import {Router} from "@angular/router"; describe('LandingpageComponent', () => { let component: LandingpageComponent; let fixture: ComponentFixture; + let router = { + navigate: jasmine.createSpy('navigate'), + routerState: jasmine.createSpy('routerState') + } beforeEach(async () => { await TestBed.configureTestingModule({ + providers: [{provide: Router, useValue: router}], declarations: [LandingpageComponent], imports: [ RouterTestingModule diff --git a/Frontend/tsconfig.spec.json b/Frontend/tsconfig.spec.json index 092345b..fca328b 100644 --- a/Frontend/tsconfig.spec.json +++ b/Frontend/tsconfig.spec.json @@ -1,18 +1,18 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/spec", - "types": [ - "jasmine" + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" ] - }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] }