How to access private method in spec file (Karma - Angular) [duplicate] - javascript

I have been going round in circles trying to unit test a Service (AuthService) that depends upon AngularFireAuth.
I am trying to find a way to mock, or highjack the Observable AngularFireAuth.authState instead of the Service actually talking to Firebase.
Here is my test spec:
import { inject, TestBed } from '#angular/core/testing';
import { AngularFireModule } from 'angularfire2';
import { AngularFireAuth, AngularFireAuthModule } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import 'rxjs/add/observable/of';
// import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Rx';
import { AuthService } from './auth.service';
import { environment } from '../../environments/environment';
const authState: firebase.User = null;
const mockAngularFireAuth: any = { authState: Observable.of(authState) };
describe('AuthService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AngularFireModule.initializeApp(environment.firebaseAppConfig)],
providers: [
{ provider: AngularFireAuth, useValue: mockAngularFireAuth },
AuthService
]
});
});
it('should be defined', inject([ AuthService ], (service: AuthService) => {
expect(service).toBeDefined();
}));
it('.authState should be null', inject([ AuthService ], (service: AuthService) => {
expect(service.authState).toBe(null);
}));
});
And here is my (simplified) Service:
import { Injectable } from '#angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class AuthService {
private authState: firebase.User;
constructor(private afAuth: AngularFireAuth) { this.init(); }
private init(): void {
this.afAuth.authState.subscribe((authState) => {
if (authState === null) {
this.afAuth.auth.signInAnonymously()
.then((authState) => {
this.authState = authState;
})
.catch((error) => {
throw new Error(error.message);
});
} else {
this.authState = authState;
}
}, (error) => {
throw new Error(error.message);
});
}
public get currentUser(): firebase.User {
return this.authState ? this.authState : undefined;
}
public get currentUserObservable(): Observable<firebase.User> {
return this.afAuth.authState;
}
public get currentUid(): string {
return this.authState ? this.authState.uid : undefined;
}
public get isAnonymous(): boolean {
return this.authState ? this.authState.isAnonymous : false;
}
public get isAuthenticated(): boolean {
return !!this.authState;
}
public logout(): void {
this.afAuth.auth.signOut();
}
}
I get the error Property 'authState' is private and only accessible within class 'AuthService'.
Of course it is, but I don't want to actually access it — I want to mock or highjack it so I can control it's value from within my test spec. I believe I am way off-course with my code here.
Please note I am using version ^4 of AngularFire2 and there were breaking changes introduced; documented here: https://github.com/angular/angularfire2/blob/master/docs/version-4-upgrade.md

Encapsulated members can be reflected.
The hard way:
expect(Reflect.get(service, 'authState')).toBe(null);
The easy way:
expect(service['authState']).toBe(null);
expect((service as any).authState).toBe(null);

Related

Angular 6 : Issue of component data binding

I call service which make http call, I assign response to component variable now when I try access that component variable to view it display blank.
Means component variable assign in subscribe successfully but cant acceess in html view.
I think view is loaded before values assign to component data.
component
import {Component, OnInit, ChangeDetectionStrategy} from '#angular/core';
import { UserService } from '../../../../../core/services/users/user.service';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'm-user-list',
templateUrl: './user-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent implements OnInit {
list;
roles = {};
current_page: any
totalRecords: any
public showContent: boolean = false;
constructor(private userService: UserService, private http: HttpClient) {
}
ngOnInit() {
this.getRecords();
}
getRecords(){
this.getResultedPage(1);
}
getResultedPage(page){
return this.userService.getrecords()
.subscribe(response => {
this.list = response.data;
});
}
}
Service
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpParams , HttpErrorResponse, HttpHeaders } from '#angular/common/http';
import { map, catchError, tap, switchMap } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
import { UtilsService } from '../../services/utils.service';
import { AppConfig } from '../../../config/app'
#Injectable({
providedIn: 'root'
})
export class UserService{
public appConfig: AppConfig;
public API_URL;
constructor(private http: HttpClient, private util: UtilsService) {
this.appConfig = new AppConfig();
this.API_URL = this.appConfig.config.api_url;
}
private extractData(res: Response) {
let body = res;
return body || { };
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
getrecords(): Observable<any> {
return this.http.get('/api/users', httpOptions).pipe(
map(this.extractData),
catchError(this.handleError));
}
}

Can't check if profile exist in firebase with Angular [Ionic]

Please, I need a help, I'm trying to check if the user profile exists in Firebase database with Angular (Ionic). so if it exists then go to TabsPage, if not go to EditProfiePage.
But it didn't work. it's returning null! and always go to EditProfilePage, Please check codes below:
this.data.getProfile(<User> event.result).subscribe(profile =>{
console.log(profile);
if (profile.hasOwnProperty('$value') && !profile['$value'] )
{
this.navCtrl.setRoot("TabsPage")
}
else {
this.navCtrl.setRoot("EditProfilePage");
}
Data Provider:
public getProfile(user: User){
this.profileObject = this.database.object(`profiles/${user.uid}`);
return this.profileObject.snapshotChanges().pipe(first());
}
See the picture:
Picture: The result is null
Thanks for all answers.
I solved the problem by the following way:
1- I imported Subscription then I declared 'authenticatedUser$' var of type Subscription, then I declared 'authenticatedUser' var of type User (firebase User). - "login.ts"
2- In Constructor I will get current Authenticated User inside 'authenticatedUser$' var, then I will subscribe it into 'authenticatedUser' var. - "AuthProvider.ts"
3- last thing, I will send authenticatedUser var as a parameter to my DataProvider, and also I will subscribe returned value. - "DataProvider.ts"
AuthProvider:
import { LoginResponse } from '../../models/login-response';
import { Injectable } from '#angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import { Account } from '../../models/account';
....................
constructor(public auth:AngularFireAuth) {
console.log('Hello AuthProvider Provider');
}
public getAuthenticatedUser(){
return this.auth.authState;
}
DataProvider:
import { Injectable } from '#angular/core';
import { AngularFireDatabase, AngularFireObject, AngularFireList } from 'angularfire2/database';
import { User } from 'firebase/app';
import { Profile } from '../../models/profile';
................
profileObject: AngularFireObject<any>;
constructor(public database: AngularFireDatabase) {
}
public getProfile(user: User){
this.profileObject = this.database.object(`profiles/${user.uid}`);
return this.profileObject.valueChanges();
}
login.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ToastController } from 'ionic-angular';
import { LoginResponse } from '../../models/login-response';
import { DataProvider } from '../../providers/data/data';
import { User } from 'firebase/app';
import { Subscription } from 'rxjs/Subscription';
import { AuthProvider } from '../../providers/auth/auth';
................
user = {} as User;
private authenticatedUser$: Subscription;
private authenticatedUser: User;
constructor(public navCtrl: NavController,
public toast:ToastController, public navParams: NavParams, public data: DataProvider,
private myAuthProvider:AuthProvider) {
this.authenticatedUser$ = this.myAuthProvider.getAuthenticatedUser()
.subscribe((user: User)=>{
this.authenticatedUser = user
})
this.data.getProfile(this.authenticatedUser).subscribe(profile =>{
console.log(profile);
if (profile) {
this.navCtrl.setRoot("TabsPage")
} else {
this.navCtrl.setRoot("EditProfilePage");
}
});
}
In your data provider
Instead of
return this.profileObject.snapshotChanges().pipe(first());
You can try
return this.profileObject.valueChanges();
There's no need to check if object has property. A simple if statement is enough.
public getProfile(user: User){
this.profileObject = this.database.object(`profiles/${user.uid}`);
return this.profileObject.valueChanges();
}
The if statement checks if the profile exists or not (If it doesn't exist the result is undefined or null)
this.data.getProfile(user).subscribe(profile =>{
if (profile) {
this.navCtrl.setRoot("TabsPage")
} else {
this.navCtrl.setRoot("EditProfilePage");
}
});

angular resolver does not have access to angular service properties

i have an anagular service called authservice ,which looks like this:
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { JwtHelperService} from '#auth0/angular-jwt';
#Injectable()
export class AuthService {
public DecdedToken: any;
userToken: any;
public userid: any;
helper: any = new JwtHelperService();
baseUrl = 'http://localhost:5000/api/auth/';
// userToken: any;
constructor(private http: Http , private jwthelpservicee: JwtHelperService) {}
login(model: any) {
return this.http.post(this.baseUrl + 'login', model, this.requestOptions()).map((response: Response) => {
const user = response.json();
if (user && user.stringToken) {
this.userToken = user.stringToken;
localStorage.setItem('token', user.stringToken);
this.DecdedToken = this.helper.decodeToken(user.stringToken);
this.userid = this.DecdedToken.nameid;
// console.log(this.userid);
// onsole.log('so far so good');
}
}).catch(this.HandleError);
}
register(model: any) {
return this.http.post(this.baseUrl + 'register', model, this.requestOptions()).catch(this.HandleError);
}
private requestOptions() {
const headers = new Headers({ 'Content-type': 'application/json' });
return new RequestOptions({ headers: headers });
}
IsLoggedIn() {
return !this.jwthelpservicee.isTokenExpired();
}
private HandleError(error: any) {
const applicationerror = error.headers.get('Application-Error');
if (applicationerror) {
return Observable.throw(applicationerror);
}
const serverError = error.json();
let modelStateErrors = '';
if (serverError) {
for (const key in serverError) {
if (serverError[key]) {
modelStateErrors += serverError[key] + '\n';
}
}
}
return Observable.throw(modelStateErrors || 'server error');
}
}
in login method DecodedToken gets its value.
i have a resolver too, in which i am trying to get a value of a property from authsercvice
import { Resolve, Router, ActivatedRouteSnapshot } from '../../../node_modules/#angular/router';
import { User } from '../_Models/user';
import { Injectable } from '../../../node_modules/#angular/core';
import { UserService } from '../_services/User.service';
import { AlertifyService } from '../_services/alertify.service';
import { Observable } from '../../../node_modules/rxjs';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import { AuthService } from '../_services/auth.service';
#Injectable()
export class MemberEditResolver implements Resolve<User> {
constructor(private userservice: UserService ,
private router: Router ,
private alertify: AlertifyService,
private authservice: AuthService) {
}
resolve(route: ActivatedRouteSnapshot): Observable<User> {
console.log(this.authservice.userid);
return this.userservice.getuser(this.authservice.DecdedToken.nameid).catch(error => {
this.alertify.error('error');
this.router.navigate(['/members']);
return Observable.of(null);
});
}
}
as you can see i have defined DecdedToken as a public property, inside the authservice this property has value, but when i want to access it from resolver ,i get null or undefined...

Angular 5 component expecting an argument

Im trying a simple profile app, and all the sudden Im getting error TS2554
ERROR in /app/components/profile/profile.component.ts(25,3): error TS2554: Expected 1 arguments, but got 0.
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { FlashMessagesService } from 'angular2-flash-messages';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object;
constructor(
private auth: AuthService,
private flashMsg: FlashMessagesService,
private router: Router
) {
}
ngOnInit() {
this.auth.getProfile().subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
}
}
auth.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable()
export class AuthService {
authToken: any;
user: any;
constructor(
private http: Http
) {
}
getProfile(user) {
let headers = new Headers();
this.loadToken();
headers.append('Authorization', this.authToken);
headers.append('Content-Type','application/json');
return this.http.get('http://localhost:3000/users/profile', {headers:headers})
.map(res => res.json());
}
loadToken() {
const token = localStorage.getItem('id_token');
this.authToken = token;
}
}
Your getProfile is expecting an argument named user but you are not passing it from the component
You need to pass an argument as follows,
this.auth.getProfile(user).subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
or if you don't need an argument , remove it from your service method.

Restricting List of Items by User Account ID

Using Angular 4, Ngrx Store & AngularFire2
I am having real problems understanding how I can restrict a list of items from Firebase based on the currently logged in user account id.
I am using ngrx as well including ngrx effects.
The steps I need to follow are:
• Get Current Users UID – Auth Object
• Get User Object based on the UID from step above
• Get Company List based on the User Account ID Above
My problem is that because I am calling firebase as an Observable the call to company list is being made before I complete the first two steps.
The code is as per below, if someone can assist that would be appreciated:
The problem is in the getEntityList Method on the generic firebase service - I have marked where the problem is
Company Component
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { Store } from '#ngrx/store';
import { AppState } from './../../../core/models/index';
import { CompanyModel } from './../../../core/models/index';
import { getCompanies} from './../../../core/store/actions/company.actions';
#Component({
selector: 'mj-company',
templateUrl: './company.component.html',
styleUrls: ['./company.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CompanyComponent implements OnInit {
entityList$: Observable<CompanyModel[]>;
constructor(private store: Store<AppState>) {
this.entityList$ = this.store.select(state => state.companies);
}
ngOnInit() { this.store.dispatch(getCompanies()); }
}
Company Actions
import { CompanyModel } from './../../models';
import { Action } from '#ngrx/store';
export const ActionTypes = {
GET_COMPANIES: 'GET_COMPANIES',
GET_COMPANIES_SUCCESS: 'GET_COMPANIES_SUCCESS',
GET_COMPANIES_ERROR: 'GET_COMPANIES_ERROR'
};
export function getCompanies() {
return {
type: ActionTypes.GET_COMPANIES,
entityRef: 'companys'
}
}
}
Company Reducer
import { ActionReducer, Action } from '#ngrx/store';
import { ActionTypes } from '../actions/company.actions';
import { CompanyModel } from '../../models';
export function companyReducer(state = [<CompanyModel>{}], action: Action) {
switch (action.type) {
case ActionTypes.GET_COMPANIES:
return action.payload;
case ActionTypes.GET_COMPANIES_SUCCESS:
return action.payload;
case ActionTypes.GET_COMPANIES_ERROR:
return action.payload;
default:
return state;
}
};
Company Effect
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { ActionTypes } from '../actions/company.actions';
import { Actions, Effect } from '#ngrx/effects';
import { FirebaseDataService } from './../../services/firebase-data.service';
#Injectable()
export class CompanyEffects {
constructor(
private actions$: Actions,
private firebaseDataService: FirebaseDataService
) { }
// tslint:disable-next-line:member-ordering
#Effect() getCompanies$ = this.actions$
.ofType(ActionTypes.GET_COMPANIES)
.switchMap(action =>
this.firebaseDataService.getEntityList(action.entityRef)
.map(companies => ({ type: ActionTypes.GET_COMPANIES_SUCCESS, payload: companies }))
.catch(() => Observable.of({ type: ActionTypes.GET_COMPANIES_ERROR })));
Firebase Generic Data Service
import { Injectable } from '#angular/core';
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2/database';
import { Observable } from 'rxjs/Rx';
import { AuthService } from './auth.service';
import { FirebaseUtilityService } from './../../core/services/firebase-utility.service';
import { UserModel, CompanyModel } from './../models/index';
#Injectable()
export class FirebaseDataService {
$key: string;
loginId: string;
currentUser: any;
constructor(private db: AngularFireDatabase,
private authService: AuthService,
private firebaseService: FirebaseUtilityService) { }
// Return an observable list with optional query
getEntityList(firebaseRef: string, query = {}): FirebaseListObservable<any[]> {
this.loginId = this.authService.currentUserId;
// I get this instantly which is good
console.log('logId: ', this.loginId);
this.currentUser = this.db.object('users/' + this.loginId);
console.log('accountId: ', this.currentUser.accountId);
// This is where the problem is because at this stage the subscription above is not complete so accountId is undefined.
return this.db.list(firebaseRef, {
query: {
orderByChild: 'accountId',
equalTo: this.currentUser.accountId
}
});
// return this.db.list(firebaseRef, query);
}
// Return a single observable item
getEntity(firebaseRef: string, key: string): FirebaseObjectObservable<any> {
const itemPath = `${firebaseRef}/${key}`;
return this.db.object(itemPath)
}
// Default error handling for all actions
private handleError(error) {
console.log(error)
}
}
Problem solved - using switchMap
return this.currentUser.switchMap(user => {
return this.db.list(firebaseRef, {
query: {
orderByChild: 'accountId',
equalTo: user.accountId
}
});
})

Categories

Resources