I am using Angular 6 with rxjs^6.0.0 and rxjs-compat^6.5.2 but I am getting the following error which I can't resolve:
rxjs__WEBPACK_IMPORTED_MODULE_1__.Observable.throw is not a function
I have tried the following solutions with no success:
"rxjs" observable.throw is not a function - Angular4
TypeError: rxjs__WEBPACK_IMPORTED_MODULE_2__.Observable.throw is not a function
https://github.com/reactivex/rxjs/issues/4070
This is the code that I'm using:
import { Injectable, Inject } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { BehaviorSubject, throwError } from 'rxjs';
import { Observable } from 'rxjs/Observable';
import { map, tap, mergeMap } from 'rxjs/Operators';
import { Login } from "./login/login";
import { CurrentUser } from "./login/current-user";
import { Registration } from "./registrations/registration";
#Injectable()
export class AuthenticationService {
private currentUserSubject: BehaviorSubject<CurrentUser>;
public currentUser: Observable<CurrentUser>;
constructor(private httpClient: HttpClient,
#Inject("BASE_API_URL") private baseUrl: string) {
this.currentUserSubject = new BehaviorSubject<CurrentUser>(JSON.parse(localStorage.getItem('currentUser')));
this.currentUser = this.currentUserSubject.asObservable();
}
public get currentUserValue(): CurrentUser {
return this.currentUserSubject.value;
}
signIn<T>(login: Login) {
return this.httpClient.post<T>(`${this.baseUrl}api/authentication/login`, login)
.pipe(mergeMap(loginResult => {
return this.httpClient.get<Registration>(`${this.baseUrl}api/users/${loginResult.user.id}/registration`)
.pipe(map(registration => {
if (registration.registrationStatusId == 1)
return throwError('Registration is pending approval.');
else if (registration.registrationStatusId == 3)
return throwError('Registration has been rejected.');
let currentUser = new CurrentUser();
currentUser.identity = registration;
currentUser.identity.user = loginResult.user;
currentUser.token = loginResult.token;
currentUser.refreshToken = loginResult.refreshToken;
localStorage.setItem('currentUser', JSON.stringify(currentUser));
this.currentUserSubject.next(currentUser);
}));
}));
}
}
Your import code is correct but I think you should use same version for rxjs and rxjs-compact so update your package.json
"rxjs": "^6.5.2",
"rxjs-compat": "^6.5.2"
Then delete package-lock.json then run npm install again
Related
I'm building an Angular Universal App. I recently added a route resolver and started seeing this weird behavior where when I run npm run dev:ssr, the page won't load until I click the reload button twice.
1st: click: browser spins and doesn't seem to timeout...
2nd click: page loads
Here is my github repo. I suspect it has something to do with my route resolver which simply fetches data from Firestore and places it in the TransferState.
Here's the resolver for convenience:
import { Inject, Injectable, PLATFORM_ID } from '#angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '#angular/router';
import { Observable, of } from 'rxjs';
import { isPlatformServer } from '#angular/common';
import { makeStateKey, TransferState } from "#angular/platform-browser";
import { Restaurant } from '../restaurants/Interfaces.model';
import { AngularFirestore } from '#angular/fire/firestore';
import { first, tap } from 'rxjs/operators';
#Injectable()
export class RestaurantResolver implements Resolve<Restaurant> {
constructor(
public afs: AngularFirestore,
private transferState: TransferState,
#Inject(PLATFORM_ID) private platformId) {
}
async resolve(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Promise<Restaurant> {
console.log('platformId', this.platformId)
const rId = route.url[1].path;
const restaurantId = makeStateKey<Restaurant>("restaurant-" + rId);
if (this.transferState.hasKey(restaurantId)) {
console.log('has key', restaurantId)
const restaurant = this.transferState.get(restaurantId, null);
this.transferState.remove(restaurantId);
return restaurant;
}
else {
let result: Restaurant = (await this.afs.doc('restaurants/' + rId).get().toPromise()).data() as Restaurant
if (isPlatformServer(this.platformId)) {
this.transferState.set(restaurantId, result);
}
return result;
}
}
}
It turns out there is a bug in the AngularFire library where observables are not completing, this the given behavior.
I am trying to do unit testing by using Karma/Jasmine, I am getting error
Argument of type 'Observable' is not assignable to parameter of type 'LoginRequest'. Property 'userid' is missing in type 'Observable'
I am getting the error in compile time in login.service.spec.ts where i am calling service.login(loginRequest);.
login.service.spec.ts
import { ComponentFixture, TestBed, inject } from '#angular/core/testing';
import { ApiConnectorService } from '../api-handlers/api-connector.service';
import { LoginService } from './login.service';
import { HttpClient, HttpHandler } from '#angular/common/http';
import { Observable } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { LoginResponse, LoginRequest } from './login.contract';
class ApiConnectorServiceStub {
constructor() { }
post(address: string, payload: LoginRequest): Observable<LoginResponse> {
let str:LoginResponse = {token:'success'};
return of(str);
}
}
describe('LoginService', () => {
let service: LoginService;
let fixture: ComponentFixture<LoginService>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [LoginService, ApiConnectorService, HttpClient, HttpHandler,
{provide: ApiConnectorService, useClass: ApiConnectorServiceStub}]
});
});
it('should be created', inject([LoginService], (service: LoginService) => {
expect(service).toBeTruthy();
}));
it('should call post on apiConnectorService with right parameters when login is called',
inject([LoginService], (service: LoginService) => {
const apiConnectorStub = TestBed.get(ApiConnectorService);
const str:LoginResponse = {token:'success'};
const spy = spyOn(apiConnectorStub, 'post').and.returnValue(of(str));
const lRequest: LoginRequest={userid:'spraju#gmail.com',password:'hsjshsj',newpassword:'hsjshsj'};
const loginRequest = of(lRequest);
service.login(loginRequest);
expect(spy).toHaveBeenCalledWith('/api/login', loginRequest);
}));
});
login.service.ts
import { Injectable } from '#angular/core';
import { ApiConnectorService } from '../api-handlers/api-connector.service';
import { LoginRequest, LoginResponse } from './login.contract';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
import * as marked from 'marked';
#Injectable()
export class LoginService {
constructor(private apiConnector: ApiConnectorService) { }
login(payload: LoginRequest): Observable<LoginResponse> {
console.log('Login payload ', payload);
return this.apiConnector.post('/api/login', payload)
.pipe(
map((data: LoginResponse) => data)
)
}
}
login.contract.ts
export interface LoginRequest {
env?: string;
userid: string;
password: string;
newpassword: string;
}
export interface LoginResponse {
token: string;
}
api-connector.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams } from '#angular/common/http';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { Observable } from 'rxjs/Observable';
import {environment} from '../../../environments/environment';
import { catchError } from 'rxjs/operators/catchError';
#Injectable()
export class ApiConnectorService {
constructor(private http: HttpClient) { }
private getQueryString(params): string {
const queryString = Object.keys(params).map(key => key + '=' + params[key]).join('&');
console.log('QUERY STRING', queryString);
return ('?' + queryString);
}
private formatErrors(error: any) {
return new ErrorObservable(error.error);
}
post(path: string, body: Object): Observable<any> {
// console.log('API SERVICE BODY', body)
return this.http.post(
`${environment.base_url}${path}`,
body
).pipe(catchError(this.formatErrors));
}
}
In your test, touch are doing
const loginRequest = of(lRequest);
service.login(loginRequest)
but the login method takes a LoginRequest as a parameter - you are attempting to pass an Observable
So change
const loginRequest = of(lRequest);
to
const loginRequest = lRequest;
Hello, There is a problem with a project that does not recognize a json file - and I do not know why. Is there anything I need to change or make it work?
this is my folders:
this is my service:
import { Injectable } from "#angular/core";
import { Ibrides } from "./brides";
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
#Injectable()
export class brideService {
private _brideUrl = 'api/brides.json';
constructor(private _http: HttpClient) { };
getBrides(): Observable<Ibrides[]> {
return this._http.get<Ibrides[]>(this._brideUrl)
.do(data => console.log('All:' + JSON.stringify(data)))
.catch(this.handleError)
}
private handleError(err: HttpErrorResponse) {
console.log(err.message);
return Observable.throw(err.message);
}
}
this is my component
import { Component, OnInit } from '#angular/core';
import { Ibrides } from "./brides";
import { brideService } from "./brides.service"
#Component({
selector: 'pm-brides',
templateUrl: './brides_list.component.html',
styleUrls: []
})
export class bridesListComponent implements OnInit {
constructor(private _brideService: brideService) {
}
errorMessage: string;
brides: Ibrides[] = [];
ngOnInit(): void {
this._brideService.getBrides()
.subscribe(brides => {
this.brides = brides
},
error => this.errorMessage = <any>error);
}
}
Just reference the file from the root level like this:
_brideUrl = 'app/api/brides.json'
For more information you can refer to this.
I am a beginner to ionic 2 unit testing. I followed angular 2 documentation (https://angular.io/docs/ts/latest/guide/testing.html) to test my ionic 2 application with karma and jasmine.
But now I am stuck in an error called
'Cannot read property '_getPortal' of undefined'
here is my LocationSearchModal.ts file
import { Component } from '#angular/core';
import { NavController, ViewController } from 'ionic-angular';
import { Location } from '../../services/domain/Location';
import { LocationService } from '../../services/LocationService';
import { LoadingController } from 'ionic-angular';
#Component({
selector: 'location-search-modal',
templateUrl: 'location-search-modal.html'
})
export class LocationSearchModal {
locationList: Array<Location> = new Array<Location>();
selectedLocation: number;
temp: any = "test";
constructor(public navCtrl: NavController, public locationService: LocationService, public viewController: ViewController, public loadingController: LoadingController) {
this.filterLocationsForString();
}
filterLocations(event: any): void {
const searchString: string = event.target.value;
this.filterLocationsForString(searchString);
console.log(this.filterLocationsForString(searchString));
}
filterLocationsForString(searchString?: string) {
let loader = this.loadingController.create({
content: "loading"
});
loader.present();
this.locationService.getLocationsForLikeSearchString(searchString)
.subscribe((result) => {
loader.dismissAll();
this.locationList = result
});
console.log(this.locationList);
}
closeLocationSearch() {
this.locationService.getLocationById(this.selectedLocation)
.subscribe((location) => this.viewController.dismiss(location[0]));
}
}
and I used service called locationService.ts there and this is that service
import { Injectable } from '#angular/core';
import { Location } from './domain/Location';
import { DatabaseAccessor } from '../database/DatabaseAccessor';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class LocationService {
locationList:Array<Location> = new Array<Location>();
constructor(public databaseAccessor: DatabaseAccessor) {}
getLocationsForLikeSearchString(searchString: string) : Observable<Array<Location>> {
const searchValue = (searchString == null) ? '%' : searchString.trim() + '%';
return <Observable<Array<Location>>> Observable.fromPromise(this.databaseAccessor.runSelectQuery(Location, new Location(), 'WHERE name LIKE ?', [searchValue]));
}
getLocationById(id: number): Observable<Location> {
return <Observable<Location>> Observable.fromPromise(this.databaseAccessor.runSelectQuery(Location, new Location(), 'WHERE id = ?', [id]));
}
saveLocations(locations: Array<Location>){
this.databaseAccessor.runInsertBatchQuery(Location.prototype, locations);
}
}
Finally, I wrote a spec.ts file to unit testing and here is that,
import { ComponentFixture, async } from '#angular/core/testing';
import { LocationSearchModal } from './LocationSearchModal';
import { LocationService } from '../../services/LocationService';
import { TestUtils } from '../../test';
import { TestBed } from '#angular/core/testing';
import { App, NavController, Platform, Config, Keyboard, Form, IonicModule, GestureController, ViewController, LoadingController } from 'ionic-angular';
import { ConfigMock } from '../../mocks';
import { TranslateModule } from 'ng2-translate';
import { DatabaseAccessor } from '../../database/DatabaseAccessor';
let comp: LocationSearchModal;
let fixture: ComponentFixture<LocationSearchModal>;
let instance: any = null;
describe('LocationSearchModal', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LocationSearchModal], // declare the test component
providers: [App, Platform, Form, Keyboard, NavController, GestureController, LoadingController, LocationService, DatabaseAccessor,
{ provide: ViewController, useClass: class { ViewController = jasmine.createSpy("viewController"); } },
{ provide: Config, useClass: ConfigMock },
],
imports: [
IonicModule,
TranslateModule.forRoot(),
],
});
fixture = TestBed.createComponent(LocationSearchModal);
comp = fixture.componentInstance;
}));
console.log(comp);
it('Testing Location Component', () => {
expect(comp.temp).toBe('test');
})
});
when I am running the following error comes from the terminal.
(my unit testing configuration are correct and I tested it with another simple .spec.ts file)
the error
SUMMARY:
✔ 1 test completed
✖ 1 test failed
FAILED TESTS:
LocationSearchModal
✖ Testing Location Component
Chrome 54.0.2840 (Linux 0.0.0)
Failed: Error in ./LocationSearchModal class LocationSearchModal_Host - inline template:0:0 caused by: Cannot read property '_getPortal' of undefined
TypeError: Cannot read property '_getPortal' of undefined
at App.present (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/~/ionic-angular/components/app/app.js:78:0 <- src/test.ts:2091:35)
at Loading.present (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/~/ionic-angular/components/loading/loading.js:31:0 <- src/test.ts:38779:26)
at LocationSearchModal.filterLocationsForString (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/src/pages/location-search/LocationSearchModal.ts:9:4184 <- src/test.ts:18993:4170)
at new LocationSearchModal (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/src/pages/location-search/LocationSearchModal.ts:9:3407 <- src/test.ts:18993:3391)
at new Wrapper_LocationSearchModal (/DynamicTestModule/LocationSearchModal/wrapper.ngfactory.js:7:18)
at _View_LocationSearchModal_Host0.createInternal (/DynamicTestModule/LocationSearchModal/host.ngfactory.js:16:35)
at _View_LocationSearchModal_Host0.AppView.create (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/~/#angular/core/src/linker/view.js:84:0 <- src/test.ts:52350:21)
at _View_LocationSearchModal_Host0.DebugAppView.create (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/~/#angular/core/src/linker/view.js:294:0 <- src/test.ts:52560:44)
at ComponentFactory.create (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/~/#angular/core/src/linker/component_factory.js:152:0 <- src/test.ts:32035:36)
at initComponent (webpack:/media/dilanka/Stuff/CODE%20BASE/Inspection/Unit%20Testing/Inspection-Rewrite/~/#angular/core/bundles/core-testing.umd.js:855:0 <- src/test.ts:7416:53)
Mock the LoadingController if the problem is from using LoadingController
export class LoadingControllerMock {
_getPortal(): any { return {} };
create(options?: any) {
return new LoadingMock()
};
}
class LoadingMock {
present() { };
dismiss() { };
dismissAll() { };
}
Import the Mock and the actual from wherever
import { LoadingController } from 'ionic-angular';
import { LoadingControllerMock } from '../../../../test-config/mocks-ionic';
Substitute
providers: [
{ provide: LoadingController, useClass: LoadingControllerMock }
]
I Solved this problem finally. I used a mock and defined required methods in that mock. Then It works :)
here is an example for a mock.
export class ViewControllerMock {
public _setHeader(): any { return {} };
public _setNavbar(): any { return {} };
public _setIONContent(): any { return {} };
public _setIONContentRef(): any { return {} };
}
then have to import that mock into your .spec.ts file as follows
import {ViewControllerMock} from '../../mocks';
then have to define that mock in your providers in spec.ts file as follows
providers: [{ provide: ViewController, useClass: ViewControllerMock}],
I have a very common/simple scenario. I have an http service which makes a call to local json files and get data which will ultimately be replaced by actual api calls.
I want to mock the service and write test on ngOnInit of Component but it seems its not working.
factory-form.component.ts
import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { Factory } from './factory';
import { FactoryService } from './factory.service';
#Component({
moduleId: module.id,
selector: 'factory-form',
templateUrl: './factory-form.component.html',
styleUrls: ['./factory-form.component.css'],
providers: [FactoryService]
})
export class FactoryFormComponent implements OnInit {
private model: Factory = new Factory();
countries;
factoryStatuses;
productTypes;
risks;
private errorMessage: string;
private submitted: boolean = false;
private active: boolean = true;
constructor(private factoryService: FactoryService) {
}
ngOnInit(): void {
this.getCountries();
}
private getCountries() {
this.factoryService.getCountry()
.subscribe(countries => this.countries = countries,
error => this.errorMessage = error);
}
onSubmit(): void {
this.submitted = true;
this.factoryService.saveFactory(this.model);
}
}
factory.service.ts
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Factory } from './factory';
import { IDictionary } from '../shared/dictionary';
import { AppSettings } from '../shared/app.constants';
#Injectable()
export class FactoryService {
constructor(private http: Http) {
}
getCountry(): Observable<IDictionary[]> {
return this.http.get(AppSettings.countryUrl)
.map(this.extractData)
.do(data => console.log("get Countries: " + JSON.stringify(data)))
.catch(this.handleError);
}
private extractData(response: Response) {
let body = response.json();
return body || {};
}
private handleError(error: Response) {
console.log(error);
return Observable.throw(error.json().error || "500 internal server error");
}
}
factory.service.mock.ts
import {provide, Provider} from '#angular/core';
import {FactoryService} from './factory.service';
import * as Rx from 'rxjs/Rx';
export class MockFactoryService extends FactoryService{
public fakeResponse: any = [{"id": 1, "name": "uk"}];
public getCountry(): Rx.Observable<any> {
let subject = new Rx.ReplaySubject()
subject.next(this.fakeResponse);
return subject;
}
public setResponse(response: any): void {
this.fakeResponse = response;
}
public getProvider(): Provider {
return provide(FactoryService, { useValue: this });
}
}
factory-form.component.spec.ts
/* tslint:disable:no-unused-varfoiable */
import { By } from '#angular/platform-browser';
import { DebugElement, provide } from '#angular/core';
import {
beforeEach, beforeEachProviders,
describe, xdescribe,
expect, it, xit,
async, inject,
addProviders,
fakeAsync,
tick,
ComponentFixture,
TestComponentBuilder
} from '#angular/core/testing';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { FactoryService } from './factory.service';
import { MockFactoryService } from './factory.service.mock';
import { FactoryFormComponent } from './factory-form.component';
beforeEachProviders(() => [FactoryFormComponent, provide(FactoryService, { useClass: MockFactoryService })]);
describe('When loading the FactoryFormComponent', () => {
var builder;
beforeEach(inject([TestComponentBuilder], (tcb) => {
builder = tcb;
builder.overrideProviders(FactoryFormComponent,
[
{ provide: FactoryService, useClass: MockFactoryService }
])
.overrideTemplate('<h1>fake tempalte</h1>');
}));
it('should call ngOnInit', async(() => {
let component = builder.createAsync(FactoryFormComponent);
console.log("component?" + JSON.stringify(component));
component.then((fixture: ComponentFixture<FactoryFormComponent>) => {
console.log("inside block");
fixture.detectChanges();
var compiled = fixture.debugElement.nativeElement;
expect(fixture.componentInstance.ngOnInit).toHaveBeenCalled();
expect(fixture.componentInstance.MockFactoryService.getCountry).toHaveBeenCalled();
}).catch((error) => {
console.log("error occured: " + error);
});;
}));
});
this is the output
12 07 2016 16:54:56.314:INFO [watcher]: Changed file "C:/Projects/ethical_resourcing/src/Ethos.Client/dist/app/factory/factory-form.component.spec.js".
12 07 2016 16:54:56.393:INFO [watcher]: Changed file "C:/Projects/ethical_resourcing/src/Ethos.Client/dist/app/factory/factory-form.component.spec.js.map".
12 07 2016 16:54:56.988:WARN [web-server]: 404: /base/dist/vendor/systemjs/dist/system-polyfills.js.map
LOG: 'component?{"_handler":{"resolved":false}}'
Chrome 51.0.2704 (Windows 7 0.0.0): Executed 1 of 1 SUCCESS (0.108 secs / 0.1 secs)
for some reason its passing because its not going to the inner block. I deliberately broke the creatAsync.then() code to see what is coming as component value.
If someone has a better suggestion to mock the service that would be great.
Update: I did try with templateUrl and overriding it but no luck. maybe missing some fundamental thing.
update 2: I am using angular-cli and just call ng test.