I've recently added basic unit tests with jest and now for some reason when I run the angular app using "ng serve" my mat-icons show up as and my HttpClient calling my backend will not return any response. I am subscribed to the observable and even try to catch the empty observable with .pipe(finalize()), no response, and the network is not showing any requests being made. The terminal running ng serve also is not showing any errors.
I can confirm that when I build and serve the files through a node server for production(I know I should be using ngnx) everything works as expected. The back-end api is also responding correctly to postman and terminal curl calls.
I have a feeling it has to do with how I configured jest or something I messed up in a config file but I can't seem to figure it out. I also have no clue how this svg class="fake-testing-svg" is getting injected into the html as I cannot find it anywhere in my code.
login.component.ts
authenticateUser() : void {
// Retrieve core id and passsword from login form
let user_core_id = this.loginForm.value.user_core_id,
password = this.loginForm.value.password;
console.log("calling login service......");
this.loginService.adAuth(user_core_id, password)
.pipe(finalize(() => console.log('complete!')))
.subscribe(res => {
console.log("got a response from login service subscription......");
// Check if there was an error authenticating the user in the db
if(res.error) {
this.alertService.error("Invalid Credentials");
console.log("AD Authentication error: " + JSON.stringify(res));
}
else {
// User is authenticated in the AD
// cache user_core_id, user_display name and user AD base_dn
console.log(res.core_id + " is authenticated in the AD");
localStorage.setItem('user_core_id', res.core_id.toUpperCase());
localStorage.setItem('user_display_name', res.display_name );
localStorage.setItem('user_dn', res.base);
// Grab user data from the AD callback and send it to the db
let user_object = res;
// Update the user in the db
this.getIsAdmin(user_object);
}
},
err => {
// Error authenticating user against the AD
this.alertService.error("Invalid Credentials");
console.log("AD Auth error: " + JSON.stringify(err));
});
login.service.ts
adAuth(user_core_id: string, password: string) : Observable<any> {
console.log("made it to the login service adAuth() method....");
let body = {
user_core_id: user_core_id,
password: password
}
return this.http.post<any>(`${environment.adApiUrl}/auth/user`, body);
}
server.js (should not affect ng serve)
const express = require('express'),
bodyParser = require('body-parser'),
path = require('path'),
cors = require('cors'),
fs = require('fs');
// Set up express middleware, json parser, cors
const app = express();
app.use(bodyParser.json());
app.use(cors());
// For reading env variables
require('dotenv').config();
var staticRoot = __dirname + '/';
console.log("env: " + process.env.NODE_ENV);
if(process.env.NODE_ENV == 'production') {
app.use(express.static(path.join(staticRoot, 'dist/toolsportal-client-build-prod')));
app.use(function(req, res, next) {
// if the request is not html then move along
var accept = req.accepts('html', 'json', 'xml');
if(accept !== 'html') {
return next();
}
// if the request has a '.' assume that it's for a file, move along
var ext = path.extname(req.path);
if(ext !== '') {
return next();
}
fs.createReadStream(staticRoot + 'dist/toolsportal-client-build-prod/index.html').pipe(res);
});
}
const PORT = process.env.NODE_PORT || 3001;
app.listen(PORT, "0.0.0.0", function () {
console.log('SERVER RUNNING ON PORT:' + PORT)
});
// Kubernetes pod readiness/liveness probe
app.use('/kube/healthy', (req, res) => {
res.status(200).json({ healthy: true });
});
// Kubernetes pod readiness/liveness probe (backup)
app.get('/', (req, res) => {
res.status(200).json({ healthy: true });
});
// Catch errors
app.on('error', (error) => {
console.log("error: " + error);
});
process.on('uncaughtException', (error) => {
console.log("uncaughtException error: " + error);
});
angular.json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"toolsportal-client": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "src/api/dist/toolsportal-client-build-prod",
"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/indigo-pink.css",
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/popper.js/dist/umd/popper.min.js",
"node_modules/bootstrap/dist/js/bootstrap.js",
"node_modules/hammerjs/hammer.min.js"
]
},
"configurations": {
"qa": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.qa.ts"
}
]
},
"dev": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.dev.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "toolsportal-client:build"
},
"configurations": {
"production": {
"browserTarget": "toolsportal-client:build:production"
},
"dev": {
"browserTarget": "toolsportal-client:build:dev"
},
"qa": {
"browserTarget": "toolsportal-client:build:qa"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "toolsportal-client:build"
}
},
"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": "toolsportal-client:serve"
},
"configurations": {
"production": {
"devServerTarget": "toolsportal-client:serve:production"
}
}
}
}
}
},
"defaultProject": "toolsportal-client",
"cli": {
"analytics": "0db305fd-b474-49d5-a4ec-95ceec1c2bcf"
}
}
jest.config.js
const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');
module.exports = {
preset: 'jest-preset-angular',
roots: ['<rootDir>/src/'],
testMatch: ['**/+(*.)+(spec).+(ts)'],
setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
collectCoverage: true,
coverageReporters: ['html'],
coverageDirectory: 'coverage/my-app',
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, {
prefix: '<rootDir>/'
})
};
tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"lib": [
"es2018",
"dom"
],
"types": [
"jquery",
"bootstrap",
"node",
"jest"
]
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
tsconfig.app.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
],
"angularCompilerOptions": {
"enableIvy": false
}
}
package.json
{
"name": "toolsportal-client",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"serve-dev": "ng serve -c=dev --port=4201",
"serve-qa": "ng serve -c=qa --port=4202",
"start-node-log": "node src/api/server.js > src/api/app.log 2>&1",
"start-node": "node src/api/server.js",
"start-nodemon": "nodemon src/api/server.js",
"build": "ng build",
"build-prod": "ng build --prod",
"test": "jest",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^9.1.12",
"@angular/cdk": "^9.2.4",
"@angular/common": "^9.1.12",
"@angular/compiler": "^9.1.12",
"@angular/core": "^9.1.12",
"@angular/forms": "^9.1.12",
"@angular/localize": "^9.1.12",
"@angular/material": "^9.2.4",
"@angular/platform-browser": "^9.1.12",
"@angular/platform-browser-dynamic": "^9.1.12",
"@angular/router": "^9.1.12",
"@kolkov/angular-editor": "^1.1.2",
"@ng-bootstrap/ng-bootstrap": "^6.2.0",
"@types/bootstrap": "^4.5.0",
"@types/jquery": "^3.5.0",
"body-parser": "^1.19.0",
"bootstrap": "^4.5.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"hammerjs": "^2.0.8",
"jira-connector": "^3.1.0",
"jquery": "^3.4.1",
"jsonwebtoken": "^8.5.1",
"jwt-decode": "^2.2.0",
"ldapjs": "^1.0.2",
"mongoose": "^5.9.24",
"node-rest-client": "^3.1.0",
"popper": "^1.0.1",
"popper.js": "^1.16.0",
"rxjs": "~6.5.5",
"rxjs-compat": "^6.6.0",
"tslib": "^1.13.0",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.901.11",
"@angular/cli": "^9.1.11",
"@angular/compiler-cli": "^9.1.12",
"@angular/language-service": "^9.1.12",
"@types/jasmine": "^3.5.11",
"@types/jasminewd2": "^2.0.8",
"@types/jest": "^25.2.3",
"@types/node": "^13.13.14",
"codelyzer": "^5.2.2",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.1",
"jest": "^26.1.0",
"jest-preset-angular": "^8.2.1",
"nodemon": "^2.0.4",
"protractor": "~5.4.3",
"ts-node": "~8.8.2",
"tslint": "~6.1.1",
"typescript": "~3.8.3"
}
}
It turns out it was in app.module.ts
import { HttpClientModule } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from'@angular/common/http/testing';
to
import { HttpClientModule } from '@angular/common/http';
Apparently importing the HttpClientTestingModule in to the app.module.ts was injecting fake svg-icons and intercepting http requests. I directly imported the HttpCientTesting Module into each component testing file and everything worked fine.