Testing ionic2 component: No provider for Form - unit-testing

I have a simple Ionic2 component using the following directives:
#View({
templateUrl: 'build/components/checkinDateInput/checkinDateInput.html',
directives: [ FocusDirective, Item, Label, TextInput ],
})
When testing this I'm getting an error: No provider for Form! (Item -> Form)
I have tried adding the provider to my spec:
beforeEachProviders(() => [Form]);
However, Form is private in Ionic and as such I don't seem to be able to import it (ionic-framework/util/form.d.ts):
/**
* #private
*/
export declare class Form {
private _blur;
...
error TS2305: Module '".../node_modules/ionic-framework/ionic"' has no exported member 'Form'.
As it can't be imported, I can't mock it out in the beforeEachProviders, because Form would be undefined.
beforeEachProviders(() => [
provide(Form, {useClass: MockForm})
]);
Should I be able to import Form or am I going about this the wrong way?

Issue with Ionic2, fixed in the next release
https://github.com/driftyco/ionic/commit/d27bb628fce40c644422016d054acd5c0317810d

Related

Jest: Cannot read property of undefined when importing from own package nextjs

Got this weird bug when running the jest test, one of the UI component from a self defined UI package keeps throwing error, saying that an object in that package is undefined...
The component itself works perfectly fine, and the same component's testing logic works in another repo without nextjs, and that repo utilize #swc/jest for js transform in jest.config file.
I've also added that package itself to transformIgnorePatterns in jest-config file, but somehow the bug still presents...
The project itself is in nextjs, and below is a snapshot of the jest.config file
/** #type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
testPathIgnorePatterns: [
'<rootDir>/.next/',
'<rootDir>/node_modules/',
'<rootDir>/e2e/'
],
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['#testing-library/jest-dom/extend-expect'],
setupFiles: [require.resolve('whatwg-fetch')],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }]
},
transformIgnorePatterns: ['/node_modules/myPackage', 'jest-runner'],
testMatch: ['**/*.spec.{js,jsx,ts,tsx}'],
};
and the error itself goes Error: Uncaught [TypeError: Cannot read property 'object' of undefined], which tracks down to /node_modules/myPackage
how the package is used
import { InputBox } from 'myPackage';
const MyComponent = () => {
return (
<div>
<InputBox />
</div>
);
}
export default MyComponent;
and here's the test:
import { act, render } from '#testing-library/react';
import React from 'react';
describe('show component', () => {
it('should render', async () => {
await act(async () => {
render(
<MyComponent/>
);
});
});
});
I've found a similar question on stackoverflow Jest: Cannot read property of undefined when importing from own package
but that one is using regular js, and this one being next.js, there's really nowhere I can update .babelrc to update those configs...
Any input would be appreciated.
Update: it turns out the component that causes me bug is built on top of react-popper library, which is built on top of popper.js library. popper.js library doesn't support jsdom by default, and it requires to do jest mock. But my library is 2 layers abstractions on top of popper.js library, I'm not sure how to do that, or even if that is doable...

Unit test a typescript vue component

I need to be able test my component (methods, computed properties, data, ...). However, when I import my vue component in my unit test:
import Pagination from 'src/components/shared/pagination.vue'
import { newComponent } from '../component-factory'
describe('pagination.vue', () => {
const propsData = {
metadata: {
page: 2,
records: 155,
total: 11,
},
perPage: 15,
}
it('should have correct number of records', () => {
const ctor = Vue.extend(Pagination)
const vm = new ctor({propsData}).$mount()
expect(vm.firstRecord).toBe(16)
expect(vm.lastRecord).toBe(30)
})
...
vm is of type Vue, and thus does not have the firstRecord/lastRecord properties. Running the test with karma shows a success, but the typescript compiler spits out Errors:
ERROR in ./tests/shared/pagination.spec.ts
(16,19): error TS2339: Property 'firstRecord' does not exist on type 'Vue'.
ERROR in ./tests/shared/pagination.spec.ts
(17,19): error TS2339: Property 'lastRecord' does not exist on type 'Vue'.
I tried casting:
...
const vm = new ctor({propsData}).$mount() as Pagination
...
But that results in a warning in VSCode:
[ts] Cannot find name 'Pagination'.
And has the effect of treating vm as type any which is totally counterproductive.
I think this all stems from the fact that when using .vue files you have to add the declaration:
declare module '*.vue' {
import Vue from 'vue'
export default typeof Vue
}
Which clearly sets the type of all .vue files to Vue, which isn't exactly a lie, but isn't helpful either... Any suggestions? What am I doing wrong?
For future reference, I have attempted to use vuetype which generates .d.ts files for each .vue file, but ran into this issue. Also, there is a request to make .vue a first class citizen in the typescript ecosystem, which would eliminate this problem. And, I just added a request for a vue language service extension
Up until Vue 2.5, their TypeScript documentation page recommended exporting an interface that extends Vue if you were not going to use vue-class-component. You can export this interface to use in your tests, to cast your component instance. The recommendation has been removed from the docs, but I have not been able to figure out how to change my tests to not need the interface.
It looks like vuetype could generate these interfaces for you, but I've just been creating them manually.
Here is a greatly simplified example, but you can define anything in your interface that you would reference on vm, ie data, props, methods:
// NOTE: Make sure your interface extends `Vue`!
export interface PaginationComponent extends Vue {
firstRecord: number,
lastRecord: number
}
export default {
name: 'Pagination',
data: function() {
return {
firstRecord: 16,
lastRecord: 30,
}
}
}
For your test, you can cast the component instance to the type of your exported interface:
import Pagination, {PaginationComponent} from 'src/components/shared/pagination.vue'
describe('pagination', () => {
it('should know about component data fields', () => {
const ctor = Vue.extend(Pagination)
const vm : PaginationComponent = new ctor().$mount()
expect(vm.firstRecord).toBe(16)
expect(vm.lastRecord).toBe(30)
})
})

Angular 2 TestModuleMetadata does not have an EntryComponents property

I'm using Angular CLI 1.0.0-beta.32.2 and am writing a unit test for an Angular 2 service, which dynamically creates a Component and inserts it into another Component.
In my unit test I try to create a mock component to test my service, but the output of ng test throws an error, stating my mock Component is specified in the entryComponents property. When I try to add the Component into an entryComponents property of the TestModuleMetadata object, like this: TestBed.createTestingModule({...entryComponents: [ TestDialogComponent ]...}) I see the following error stating the entryComponents property does not exist.
Chrome 56.0.2924 (Windows 10 0.0.0) DialogService should create a child component when opening FAILED
Error: No component factory found for TestDialogComponent. Did you add it to #NgModule.entryComponents?
Looking at the TestModuleMetadata definition shows that the entryComponents property does not exist. So how do I go about dynamically creating a Component in my unit tests in Angular 2 and Jasmine?
As far as i know it has not supported yet. As workaround you can create fake module with entryComponent and import it to your testing module
#NgModule({
imports: [CommonModule],
declarations: [TestDialogComponent],
entryComponents: [TestDialogComponent]
})
export class FakeTestDialogModule {}
and then
TestBed.configureTestingModule({
imports: [FakeTestDialogModule]
Check TestBed.overrideModule. Please refer to this discussion
https://github.com/angular/angular/issues/12079
I got the error for my DialogBoxComponent. I resolved it as follows
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NewPracticeQuestionComponent,
DialogBoxComponent,
ShowErrorsComponent],
imports:[ReactiveFormsModule,HttpClientTestingModule],
providers:[WebToBackendInterfaceService,HelperService,AuthService]
})
TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {
entryComponents: [DialogBoxComponent]
}
})
.compileComponents();
}));

Ionic 2 lazy loading components

My app is really big and has like 30 components and pages, I load all of them in my app.module.ts and sometimes the application turn slow. I wonder if it has anything to do it.
My question: What's the correct way to lazy load components and use angular 2 features (more modules) with Ionic 2?
Since Ionic 3, you can lazy load components.
Simply, create a new module for each component/page.
Here's an example of how a module of the HomePage should look like:
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
#NgModule({
declarations: [MyApp, HomePage],
imports: [ ... ],
bootstrap: [IonicApp],
entryComponents: [MyApp, HomePage],
providers: [ ... ]
})
export class AppModule {}
After creating the module, attach #IonicPage() to the component:
import { Component } from '#angular/core';
import { IonicPage } from 'ionic-angular';
#IonicPage()
#Component(... )
export class HomePage { ... }
Now you can use your page/component as a string without using the import statement:
rootPage:any = 'HomePage';
for more descriptibe answer, check out this Ionic Lazy Loading blog post.

Issue with angular2 tesing DI

I try to test some component with DI. I looking in the common resources like stack/forums etc. But there is no correct answer to my question (I can't found).
When I try to provide mock dependency I got error about: Token must be defined
What is this? How I can provide mock dependency? (in provided component exist some next level dependency - from http and config, so I can create it in real (because he failed withoud his own dependencies...And I think I must to mock this dependency).
There is my test
import {provide} from 'angular2/core';
import {setBaseTestProviders} from 'angular2/testing';
import {
TEST_BROWSER_PLATFORM_PROVIDERS,
TEST_BROWSER_APPLICATION_PROVIDERS
} from 'angular2/platform/testing/browser';
import { beforeEach,beforeEachProviders,describe,expect,
provide,
it,
inject,
injectAsync,
TestComponentBuilder,
AsyncTestCompleter} from 'angular2/testing';
import {HTTPPatientsListService} from '../../shared/http_services/http_patients_list.service';
import {PatientsListComponent} from './patients_list.component';
class MockClass {}
describe('Patients list Tests', () => {
beforeEachProviders(() => [
provide(HTTPPatientsListService, {useClass: MockClass})
]);
it('Should defined recentPatientData ', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb.createAsync(PatientsListComponent).then((componentFixture: ComponentFixture) => {
const element = componentFixture.nativeElement;
componentFixture.detectChanges();
});
}));
});
There part of my component (only part. it is work correctly, buy he too long)
#Component({
selector: 'cgm_patients_list',
templateUrl: `${MODULE_PATH}/patients_list.component.html`,
styleUrls: [`..${MODULE_PATH}/patients_list.component.css`],
pipes: [SearchPipe],
providers: [HTTPPatientsListService],
directives: [PatientsListDetailComponent]
})
export class PatientsListComponent implements OnInit {
public recentPatientData;
private pipedPatientsData;
constructor(
private patientsListService: HTTPPatientsListService) {
}
thanks for any help...
P.S. Error is:
Chrome 49.0.2623 (Windows 7 0.0.0) Patients list Tests Should defined recentPatientData FAILED
Failed: Token must be defined!
Error: Token must be defined!
at new BaseException (D:/nucleous/client/src/www/node_modules/angular2/bundles/angular2.dev.js:7521:21)
You need to override the providers of the test component
return tcb
.overrideProviders(PatientsListComponent, [provide(HTTPPatientsListService, {useClass: MockClass})])
.createAsync(PatientsListComponent)
.then((componentFixture: ComponentFixture) => {
See also https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html