Karma Unit Test case coverage for lazy loading in Angular 8 - unit-testing

How to write the unit test cases for lazy loading in angular 8. i tried a lot but my unit test cases are not covering "loadChildren" routing.
import { Routes, RouterModule } from "#angular/router";
import { AppRoutingComponent } from "./app-routing.component";
const appRoutes: Routes = [
{ path: "index.html", component: AppRoutingComponent },
{ path: "product", loadChildren: () => import('app/dashboard/product.module').then(m => m.ProductModule) },
{ path: "", redirectTo: "/main", pathMatch: "full" } ];
export const routedComponents = [ AppRoutingComponent,
];
#NgModule({
imports: [routing],
exports: [RouterModule]
})
export class AppRoutingModule { }
Please provide some guidance.
Thanks in advance.

Related

After skiping the test, still jest takes long time, how to make it to 0 time

I am doing testing with jest.js in my angular project. It seems that, my whole testing process take closly 45min, which i not requried. I decided to skip some of unwanted tests from the project. for that I am skipping it out.
But still I am getting 25.36s for the skipped file. how to make it to 0s instead? here is my test case: (xdescribe) i skipped.
import { TestBed, inject, getTestBed } from '#angular/core/testing';
import { StoreModule } from '#ngrx/store';
import { RouterTestingModule } from '#angular/router/testing';
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { TranslateModule, TranslateLoader } from '#ngx-translate/core';
import { IboSharedLibModule } from '#ibo/ibo-shared-lib';
import { CourseMappingAddGuard } from './course-mapping-add.guard';
import { HttpClient } from '#angular/common/http';
import { provideMockStore, MockStore } from '#ngrx/store/testing';
import { Router } from '#angular/router';
import * as subscribe from '../previous-course-linkage/state/selectors/course-mapping.selector';
xdescribe('CourseMappingAddGuard', () => {
let injector: TestBed;
let guard: CourseMappingAddGuard;
function createTranslateLoader(http: HttpClient) {
const store: MockStore<any> = null;
}
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [],
imports: [
StoreModule.forRoot({}, { runtimeChecks: { strictStateImmutability: true, strictActionImmutability: true } }),
HttpClientTestingModule,
IboSharedLibModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient],
},
isolate: true,
}),
],
providers: [
CourseMappingAddGuard,
provideMockStore({
selectors: [],
}),
],
});
injector = getTestBed();
guard = injector.get(CourseMappingAddGuard);
});
it('should redirect to parent page if no data available', () => {
expect(guard.canActivate()).toEqual(false);
});
});
here is the test result:
$ jest course-mapping-add.guard.spec.ts
Test Suites: 1 skipped, 0 of 1 total
Tests: 1 skipped, 1 total
Snapshots: 0 total
Time: 16.232s, estimated 20s
Ran all test suites matching /course-mapping-add.guard.spec.ts/i.
Done in 25.36s.
any one please help me to understand? thanks in advance.

How to use imported graphql files in jest unit testing?

I'm trying to run unit tests with jest but I'm getting the following error:
● Test suite failed to run
/apollo/queries/articles.gql:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){query articles($orderBy: [OrderByClause!], $stripTags: Boolean, $maxCharacters: Int) {
^^^^^^^^
SyntaxError: Unexpected identifier
I have installed
https://github.com/jagi/jest-transform-graphql
It's suppose to transform GQL files.
My package.json (jest part)
"jest": {
"moduleFileExtensions": [
"js",
"json",
"vue",
"gql"
],
"watchman": false,
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/$1",
"^~~/(.*)$": "<rootDir>/$1"
},
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"\\.(gql|graphql)$": "#jagi/jest-transform-graphql"
},
"snapshotSerializers": [
"<rootDir>/node_modules/jest-serializer-vue"
],
"collectCoverage": true,
"collectCoverageFrom": [
"<rootDir>/components/**/*.vue",
"<rootDir>/pages/*.vue"
]
}
Test file
import Index from "../index";
const factory = () =>
shallowMount(Index, {
propsData: {
label: "click me!"
}
});
describe("Index", () => {
test("mounts properly", () => {
const wrapper = factory();
expect(wrapper.isVueInstance()).toBeTruthy();
});
test("renders properly", () => {
const wrapper = factory();
expect(wrapper.html()).toMatchSnapshot();
});
});
index.vue file (stripped out unimportant things)
<template>
<div></div>
</template>
<script lang="ts">
import Vue from "vue";
import ArticlesQuery from "~/apollo/queries/articles.gql";
export default Vue.extend({
name: "Homepage",
apollo: {
articles: {
query: ArticlesQuery,
variables() {
return {
orderBy: [{ field: "id", order: "DESC" }],
stripTags: true,
maxCharacters: 150
};
},
prefetch: true
}
}
});
</script>
This is my first time doing unit testing, so I have zero knowledge on this subject.
I had the same problem with Nuxt. I installed this dependence: https://www.npmjs.com/package/jest-transform-graphql, and add this: '\.(gql|graphql)$': 'jest-transform-graphql' in jest.config.js file, it works for me
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
'\\.(gql|graphql)$': 'jest-transform-graphql'
},

Is it possible to mock custom Angular 2 Material SVG icons for unit tests?

In my app's root component, I am defining custom SVG icons for md-icon. When unit testing a component that displays the custom icon I get an error. It seems that the error is likely due to the fact that my root component is not being used/initialized in my child unit test.
Is there a way to mock or add these custom icons (or md-icon) when setting up the test module? I would simply define the icons in the component I am testing, but I know other components will need them also.
The error:
Uncaught Error: Error in ./CustomerComponent class CustomerComponent - inline template:34:19 caused by: __WEBPACK_IMPORTED_MODULE_4_rxjs_Observable__.Observable.throw is not a function
Full error:
Removing the custom icons from the template solves the error.
My template is using the custom icons like this:
<md-icon svgIcon="vip">vip</md-icon>
And the root component initializes the icons like this:
this.iconRegistry.addSvgIcon(
'vip',
this.sanitizer.bypassSecurityTrustResourceUrl('assets/icons/vip.svg') as string,
);
I set up the test component like this:
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
SharedModule,
CoreModule,
FormsModule,
ReactiveFormsModule,
],
providers: [
{
provide: Router,
useClass: class {
navigate = jasmine.createSpy('navigate');
},
},
{
provide: ActivatedRoute,
useValue: {
data: {
subscribe: (fn: (value: Data) => void) => fn({
customer: CUSTOMER,
company: COMPANY,
}),
},
},
},
{
provide: UtilityService,
useClass: UtilityServiceMock,
},
// etc...
],
declarations: [
CustomerComponent,
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA,
],
})
.compileComponents();
}));
Versions
Angular 2.3.0
Material 2.0.0-beta.1
I was able to use the overrideModule method to stub MdIcon. The documentation is sparse but I was able to find a GitHub issue where Angular team members discuss how to override declarations. The idea is to remove the component from the MdIconModule so that we can declare our own mock icon component.
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestedComponent ],
imports: [
RouterTestingModule.withRoutes([]),
SharedModule,
],
})
.overrideModule(MdIconModule, {
remove: {
declarations: [MdIcon],
exports: [MdIcon]
},
add: {
declarations: [MockMdIconComponent],
exports: [MockMdIconComponent]
}
})
.compileComponents();
}));
The MockMdIconComponent is defined very simply
#Component({
selector: 'md-icon',
template: '<span></span>'
})
class MockMdIconComponent {
#Input() svgIcon: any;
#Input() fontSet: any;
#Input() fontIcon: any;
}
I used this approach because I am not importing the Material modules individually and I did not want my test to have to make Http calls to get the svg icons. The MockMdIconComponent could be declared in the testing module but I chose to declare/export it in the module override so that I could extract the object into a test helper.
Answering my own question:
After much trial/error with items like mocking the MdIconRegistry or using componentOverride() etc with no luck I no longer use a shared module within my tests.
Instead, I declare the MdIcon component directly in my testing module using a mock version of the class.
describe(`CustomerComponent`, () => {
let component: CustomerComponent;
let fixture: ComponentFixture<CustomerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
ReactiveFormsModule,
MdButtonModule,
],
providers: [
OVERLAY_PROVIDERS,
{
provide: Router,
useClass: class {
navigate = jasmine.createSpy('navigate');
},
},
{
provide: ActivatedRoute,
useValue: {
data: {
subscribe: (fn: (value: Data) => void) => fn({
customer: customer,
company: COMPANY,
}),
},
params: Observable.of({
customerId: customerId,
}),
},
},
],
declarations: [
CustomerComponent,
// Declare my own version of MdIcon here so that it is available for the CustomerComponent
MdIconMock,
],
});
fixture = TestBed.createComponent(CustomerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it(`should exist`, () => {
expect(component).toBeTruthy();
});
});
MdIconMock is simply a blank class that matches the selectors:
import { Component } from '#angular/core';
#Component({
template: '',
// tslint:disable-next-line
selector: 'md-icon, mat-icon',
})
// tslint:disable-next-line
export class MdIconMock {
}
Note: Due to TSLint rules that specify the prefix/format of class names/selectors I needed to disable TSLint for this mock.
This is a late answer. Just in case anyone come across this, there is an alternative other than OP's solution (which is nice as well):
Imports MaterialModule using forRoot()
TestBed.configureTestingModule({
declarations: [
TestComponent
],
imports: [SharedModule, MaterialModule.forRoot()],
providers: [{ provide: Router, useValue: routerStub }]
});
TestBed.compileComponents();
Get the injected MdIconRegistry and DomSanitizer
let iconRegistry = TestBed.get(MdIconRegistry);
let sanitizer = TestBed.get(DomSanitizer);
Configure them as you did in normal app
iconRegistry.addSvgIcon( 'some-icon',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/some-icon.svg'));
building on #Chic's answer, since I have icons everywhere, i made a testbed patch:
import {TestBed} from '#angular/core/testing';
import {MatIconModule, MatIcon} from '#angular/material/icon';
import {Component, Input} from '#angular/core';
export function PatchTestBedMatIcons() {
const original = TestBed.configureTestingModule;
TestBed.configureTestingModule = (moduleDef) => {
return original(moduleDef)
.overrideModule(MatIconModule, {
remove: {
declarations: [MatIcon],
exports: [MatIcon]
},
add: {
declarations: [MockMatIconComponent],
exports: [MockMatIconComponent]
}
});
};
}
#Component({
selector: 'mat-icon',
template: '<span></span>'
})
class MockMatIconComponent {
#Input() svgIcon: any = null;
#Input() fontSet: any = null;
#Input() fontIcon: any = null;
}
then in your component test simply:
import {PatchTestBedMatIcons} from 'src/app/patchTestBedIcons';
PatchTestBedMatIcons();

Angular 2 RC6: 'pattern-list' is not a known element

Simply running the application I get no error and it works just fine, but when I run my tests I get the following error:
'pattern-list' is not a known element:
1. If 'pattern-list' is an Angular component, then verify that it is part of this module.
2. If 'pattern-list' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '#NgModule.schema' of this component to suppress this message. ("
[ERROR ->]<pattern-list></pattern-list>
I had this issue first when I just run the application with 'npm-start' and I solved it adding the required component to app.module in the declarations section. But now as I want to test I get the same error and I don't know why.
Here is my code:
app.module.ts
#NgModule({
imports: [ BrowserModule, FormsModule, HttpModule, ReactiveFormsModule ],
declarations: [ AppComponent, PatternListComponent, PatternDetailComponent, WidgetListComponent,
FormComponent, DefaultWidget, LabelComponent, CheckboxWidget ],
bootstrap: [ AppComponent ],
providers: [ WidgetService ]
})
export class AppModule { }
app.component.ts
#Component({
selector: 'my-app',
template: `
<pattern-list></pattern-list>
`
})
export class AppComponent { }
pattern.list.component:
#Component({
selector: 'pattern-list',
template: `
<div class="patterns">
<pattern-detail *ngFor="let p of patternDetails" [metadata]="p"
(selectPattern)="selectPattern(p)"></pattern-detail>
</div>
<div *ngIf="selectedPattern" class="widget-list">
<widget-list [pattern]="selectedPattern">
</widget-list>
</div>
`,
styleUrls: ['/css/styles.css']
})
export class PatternListComponent implements OnInit{
selectedPattern: PatternDetails;
constructor(private http: Http) {
}
patternDetails: PatternDetails[];
ngOnInit() {
this.getPatterns();
}
getPatterns() {
this.http.get('/app/assets/patternDetails.json')
.map((res:Response) => res.json() )
.subscribe(
data => { this.patternDetails = data.patternList; },
err => console.error('The problem is: ' + err),
() => console.log('done')
);
console.log(this.patternDetails);
}
selectPattern(pattern: PatternDetails) {
this.selectedPattern = pattern;
this.setSelectedProperty(pattern);
}
setSelectedProperty(selectedPattern: PatternDetails) {
for (var p in this.patternDetails) {
if (this.patternDetails[p] == selectedPattern) {
this.patternDetails[p].selected = true;
} else {
this.patternDetails[p].selected = false;
}
}
}
}
My test file: app.component.spec.ts
describe('AppComponent with TCB', function () {
beforeEach(() => {
TestBed.configureTestingModule({declarations: [AppComponent]});
});
describe('asdfasdf', function () {
beforeEach(async(() => {
TestBed.compileComponents();
}));
it('should instantiate component', () => {
let fixture = TestBed.createComponent(AppComponent);
expect(fixture.componentInstance instanceof AppComponent).toBe(true, 'should create AppComponent');
});
});
});
I'm using webpack, I'm not sure if that matters.
I think you need
TestBed.configureTestingModule({imports: [AppModule]});
The current approach to avoid these errors when testing components is to make their tests shallow. As per the official docs:
Add NO_ERRORS_SCHEMA to the testing module's schemas metadata to tell the compiler to ignore unrecognized elements and attributes. You no longer have to declare irrelevant components and directives.
So you can simply import NO_ERRORS_SCHEMA and add it to your testing module config:
import { NO_ERRORS_SCHEMA } from '#angular/core';
TestBed.configureTestingModule({
schemas: [ NO_ERRORS_SCHEMA ]
})
But be aware of:
Shallow component tests with NO_ERRORS_SCHEMA greatly simplify unit testing of complex templates. However, the compiler no longer alerts you to mistakes such as misspelled or misused components and directives.
As micronyks mentined in his answer I need to add my other dependencies in the declarations of configureTestingModule. So if I modify my module configuration in the test like this:
TestBed.configureTestingModule({declarations: [AppComponent,PatternListComponent]});
it'll work. It seems you need to add every dependency in the configureTestingModule declaration.

Module not found: Error: Cannot resolve 'file' or 'directory'

Can you guys please help me fixing this issue.
I have two .jsx files one imported under another one.
Lets say,
A.jsx(Inside A.jsx I have imported the B.jsx)
B.jsx
When both the files are written under same file in that case unit test cases working fine. The moment I am separating it out, still the component is working fine but the unit test cases are not running. Webpack karma throwing an error saying
ERROR in ./src/components/thpfooter/index.jsx Module not found: Error: Cannot resolve 'file' or 'directory' ./ThpFooterList in /Users/zi02/projects/creps_ui_components_library/src/components/thpfooter # ./src/components/thpfooter/index.jsx 9:1725-1751
karma.conf.js
/*eslint-disable*/
var webpack = require('karma-webpack');
var argv = require('yargs').argv;
var componentName = "**";
if (typeof argv.comp !== 'undefined' && argv.comp !== null && argv.comp !== "" && argv.comp !== true) {
componentName = argv.comp;
}
var testFiles = 'src/components/'+componentName+'/test/*.js';
var mockFiles = 'src/components/'+componentName+'/test/mock/*.json';
module.exports = function (config) {
config.set({
frameworks: ['jasmine'],
files: [
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
testFiles,
mockFiles
],
plugins: [webpack,
'karma-jasmine',
'karma-phantomjs-launcher',
'karma-coverage',
'karma-spec-reporter',
'karma-json-fixtures-preprocessor',
'karma-junit-reporter'],
browsers: ['PhantomJS'],
preprocessors: {
'src/components/**/test/*.js': ['webpack'],
'src/components/**/*.jsx': ['webpack'],
'src/components/**/test/mock/*.json': ['json_fixtures']
},
jsonFixturesPreprocessor: {
// strip this from the file path \ fixture name
stripPrefix: 'src/components/',
// strip this to the file path \ fixture name
prependPrefix: '',
// change the global fixtures variable name
variableName: '__mocks__',
// camelize fixture filenames
// (e.g 'fixtures/aa-bb_cc.json' becames __fixtures__['fixtures/aaBbCc'])
camelizeFilenames: true,
// transform the filename
transformPath: function (path) {
return path + '.js';
}
},
reporters: ['spec', 'coverage','junit'],
coverageReporter: {
dir: 'build/reports/coverage',
reporters: [
{ type: 'html', subdir: 'report-html' },
{ type: 'lcov', subdir: 'report-lcov' }
]
},
junitReporter: {
outputDir: 'build/reports/coverage/junit/'+componentName,
suite: ''
},
webpack: {
module: {
loaders: [{
test: /\.(js|jsx)$/, exclude: /node_modules/,
loader: 'babel-loader'
}],
postLoaders: [{
test: /\.(js|jsx)$/, exclude: /(node_modules|test)/,
loader: 'istanbul-instrumenter'
}]
}
},
webpackMiddleware: { noInfo: true }
});
};
footer.jsx
import React from 'react';
import ThpFooterList from './ThpFooterList';
class ThpFooter extends React.Component {
//footer code here
}
ThpFooterList.jsx
import React from 'react';
class ThpFooterList extends React.Component {
//footer list code here
}
See above component is working but I am not able to execute the unit test case. When you keep both of them in one file means footer and footerlist.jsx then component as well as the unit test cases are executing.
unit test case file
/* eslint-env jasmine */
import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils';
import ThpFooter from '../index.jsx';
describe('ThpFooter', () => {
let component;
let content;
let shallowRenderer;
let componentShallow;
beforeAll(() => {
content = window.__mocks__['thpfooter/test/mock/content'];
component = TestUtils.renderIntoDocument(<ThpFooter data={content}/>);
shallowRenderer = TestUtils.createRenderer();
shallowRenderer.render(<ThpFooter data={content}/>);
componentShallow = shallowRenderer.getRenderOutput();
});
describe('into DOM', () => {
it('Should be rendered into DOM', () => {
expect(component).toBeTruthy();
});
it('Should have classname as footer-container', () => {
const classname = TestUtils.scryRenderedDOMComponentsWithClass(component, 'footer-container');
expect(classname[0].className).toBe('footer-container');
});
it('Should have className as footer-wrapper', () => {
const classname = TestUtils.scryRenderedDOMComponentsWithClass(component, 'footer-wrapper');
expect(classname[0].className).toBe('footer-wrapper');
});
});
describe('into shallow renderer', () => {
it('Should be rendered as shallow renderer', () => {
expect(componentShallow).toBeTruthy();
});
it('Should have classname as footer-container', () => {
expect(componentShallow.props.className).toBe('footer-container');
});
it('Should have className as footer-wrapper', () => {
expect(componentShallow.props.children.props.children[0].props.className).toBe('footer-wrapper');
});
});
});
I experienced the same error on one of the development machines. Although gulp and webpack-stream was used in my case, I think you may reference my method to try solving it.
On my mac, everything is fine but when I pushed the code to the ubuntu development platform, this problem was observed. After some googling I cannot solve it but then I tried to make the file path to be shorter and then suddenly it works on the ubuntu development platform too! You may try to shorten the file name or place it in a shorter path and test to see if it works.
Watch for case sensitivity. Mac file system is not case-sensitive, windows/linux is.