I'm using ionic3 and am trying to unit test (using Jasmine/karma) the following code. Can anyone offer any guidance.
createTable(databaseName: string): Promise<any> {
let sql = 'CREATE TABLE IF NOT EXISTS table)';
return new Promise((resolve, reject) => {
this.sqlite.create({
name: databaseName + '.db',
location: 'default'
}).then((db: SQLiteObject) => {
db.executeSql(sql, [])
.then(() => {
console.info('Executed SQL');
})
.catch((e) => {
console.error('createTable Error', e.message);
reject(e);
});
});
})
}
Here's my work in progress attempt to test
describe('createTable()', () => {
let db: SQLiteObject;
it('Should call function createTable', () => {
spyOn(service.sqlite, 'create').and.returnValue(Promise.resolve(
));
let databaseName = 'testDatabase';
service.createEncounterTable(databaseName);
expect(service.sqlite.create).toHaveBeenCalled();
});
});
I'm getting the following error return ERROR: 'Unhandled Promise rejection:', 'Cannot read property 'executeSql' of undefined', and dont seem to be able to gain access to executeSql.
As far as I can tell, from the lack of online resource on testing SQLite I have to assume that most people don't test this. Thanks in advance to any guidance.
Related
In my Ionic app, I apply FirebaseX plugin (https://github.com/dpa99c/cordova-plugin-firebasex) and use its method fetchDocumentInFirestoreCollection to access a document from my Firestore (assume the document does exist properly). It successfully passes the success callback function inside the method but the returned document object is never accessed. I don't know how to access it actually. Here are my two used approaches to access:
await this.firebase.fetchDocumentInFirestoreCollection(
someDocID,
'someCollection',
() => {
console.log('fetchFirestoreCollection successfully'); // this can be printed
},
error => {
console.error('error in fetchFirestoreCollection', error);
}
).then(
doc => {
// Not enter this block ever
console.log(doc);
}
);
const doc = await this.firebase.fetchDocumentInFirestoreCollection(
someDocID,
'someCollection',
() => {
console.log('fetchFirestoreCollection successfully'); // this can be printed
},
error => {
console.error('error in fetchFirestoreCollection', error);
}
);
But both of these two cannot access the returned document. How should I do?
Thank you.
In the #ionic-native/firebase-x/ngx/index.d.ts, change line 437 from
fetchDocumentInFirestoreCollection(documentId: string, collection: string, success: () => void, error: (err: string) => void): Promise<any>;
to
fetchDocumentInFirestoreCollection(documentId: string, collection: string, success: (value: object) => void, error: (err: string) => void): Promise<any>;
I have some basic code to retrieve data from my firestore data base. I am getting the correct data but for some reason that operation appears to be occurring twice.
The logs show all the documents printed out twice. This could be really troublesome with more complex operations. I feel life I'm probably doing something goofy.
exports.deleteProject = functions.firestore.document('{userID}/projects/easy/{projectID}').onDelete(event => {
.........
console.log(db)
var collectionRef = db.collection(userID).doc(xxx).collection(yyy);
console.log(collectionRef)
var getDoc = collectionRef.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
}
The "........" is just string stuff to reference to proper point in that database
Below "=>" indicates a doc pointing to its data. I cleaned it up for brevity.
Logs:
12:04:37.820 PM
info
deleteProject
tail => {
info => {
xxxxxxxxxxxxxxx => {
tail => {
info => {
xxxxxxxxxxxxxxx => {
yyyyyyyyyyyyyyy => {
I'm subscribing to a service to get an Array of Teams
teamService code:
Here my teamService code (get results from firebase):
getTeams(): Observable<Team[]> {
this.login();
let teams: Team[] = [];
return this._db.list('teams')
.map((response) => {
response.forEach(team => {
this._fb.storage().ref().child('teams/' + team.photo).getDownloadURL()
.then(imageUrl => {
teams.push(new Team(team.id, team.name, imageUrl, team.zone, team.points, team.matchesPlayed,
team.matchesWon, team.matchesDrawn, team.matchesLost, team.goalsScored,
team.goalsReceived, team.dif));
})
.catch(error => {
console.log(error);
});
});
return teams;
});
}
Here I'm subscribing to my service to get the teams:
loadStandings() {
let loading = this.loadingCtrl.create({
spinner: 'crescent'
});
loading.present();
this.teamService.getTeams().subscribe(t => {
this.teams = t;
console.log('showing this.teams');
console.log(this.teams); // this.teams declared as any[]
this.teamsOrdered = _.orderBy(this.teams, ['points', 'dif'], ['desc', 'desc']);
console.log('showing this.teamsOrdered');
console.log(this.teamsOrdered); // this.teamsOrdered declared as any[]
});
loading.dismiss();
}
console.log(this.teams) shows me the expected array returned from the service but console.log(this.teamsOrdered) is empty.
Lodash is imported import * as _ from 'lodash'; at the top of my ts file
Here screenshot from console
Edit: I added my getTeamsService code, when debugging (put a breakpoint on console.log(this.teams); I realized that the code below this.teams=t is executing before the values are retrieved from the service call.
If I don't debug then the result are in the same from my console output screenshot.
What am I doing wrong?
I am trying to get route params and then get data from the service
this.route.params
.switchMap(params => this.service.getData(params['id']))
.subscribe(value => this.value = value,
error => console.log(error));
This works fine, until first error. After the error this line doesn't calls no more params => this.noteService.GetParams(params['id']).
I can write something like this, but i think there is a better way
this.route.params.subscribe(
params => {
this.service.getData(params['id']).subscribe(
result => console.log(result),
error => console.log(error))
});
My service
public getData(id): Observable<any> {
return this.http.get('api/data/' + id)
.map(data => data.json())
.catch(error => Observable.throw(error));
}
Update
This answer helped me a lot to understand what is going on.
When I call Observable.throw(error) subscription to route params stops with an error. So instead of throwing error I just need to return empty observable.
my.component.ts
this.route.params
.switchMap(params => this.service.GetData(params['id']))
.subscribe(result => {
if (result) this.data = result;
else console.log('error');
});
my.service.ts
public GetData(id): Observable<any> {
let url = 'api/data' + id;
return this.http.get(url)
.map(data => data.json())
.catch(error => Observable.of(null));
}
I'm building a github users application right now and had the same problem.
Here is a solution that works for me:
users.service.ts
public getByUsername(username: string): Observable<any[]> {
return this.http
.get(`${this.url}/${username}`)
.map((res: Response) => res.json());
}
user.component.ts
ngOnInit() {
this.sub = this.route.params
.flatMap((v: any, index: number) => {
return this.usersService.getByUsername(v.name);
})
.subscribe(data => this.user = data);
}
So, basically the flatMap operator does the trick.
Here is link to another question,
helping me to figure out how things work with chaining RxJS Observables
I'm been using the ionic 2 native sqlite plugin to make a basic app to capture and store images to the phones local storage. I keep getting an uncaught exception error (seemingly from the sql plugin) sometimes when i run the app on the genymotion emulator.
This normally happens when the app reloads when i'm using the -live parameter(ionic run android -c -l).
I also noticed that data which stored on the app doesn't appear(this again indicates that there is some problem loading the stored data when 'live reloading')
I've put the error i get on the console below:
The Console error message
[13:28:27] Starting 'html'...
[13:28:27] Finished 'html' after 42 ms
HTML changed: www/build/pages/home/home.html
HTML changed: www/build/pages/map-detail-component/map-detail-component.html
HTML changed: www/build/pages/map-page/map-page.html
0 298305 log Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
1 298474 group EXCEPTION: Error: Uncaught (in promise): ReferenceError: sqlitePlugin is not defined
2 298475 error EXCEPTION: Error: Uncaught (in promise): ReferenceError: sqlitePlugin is not defined
3 298476 error STACKTRACE:
4 298477 error Error: Uncaught (in promise): ReferenceError: sqlitePlugin is not defined
at resolvePromise (http://192.168.56.1:8100/build/js/zone.js:418:31)
at resolvePromise (http://192.168.56.1:8100/build/js/zone.js:403:17)
at http://192.168.56.1:8100/build/js/zone.js:451:17
at ZoneDelegate.invokeTask (http://192.168.56.1:8100/build/js/zone.js:225:37)
at Object.NgZoneImpl.inner.inner.fork.onInvokeTask (http://192.168.56.1:8100/build/js/app.bundle.js:37360:41)
at ZoneDelegate.invokeTask (http://192.168.56.1:8100/build/js/zone.js:224:42)
at Zone.runTask (http://192.168.56.1:8100/build/js/zone.js:125:47)
at drainMicroTaskQueue (http://192.168.56.1:8100/build/js/zone.js:357:35)
at XMLHttpRequest.ZoneTask.invoke (http://192.168.56.1:8100/build/js/zone.js:297:25)
5 298478 groupEnd
6 298481 error Unhandled Promise rejection:, sqlitePlugin is not defined, ; Zone:, angular, ; Task:, Promise.then, ; Value:, [object Object], ReferenceError: sqlitePlugin is not defined
at http://192.168.56.1:8100/build/js/app.bundle.js:97420:13
at new ZoneAwarePromise (http://192.168.56.1:8100/build/js/zone.js:467:29)
at SQLite.openDatabase (http://192.168.56.1:8100/build/js/app.bundle.js:97419:16)
at new SqlService (http://192.168.56.1:8100/build/js/app.bundle.js:218:21)
at DebugAppView.Object.defineProperty.get (MyApp.template.js:18:67)
at DebugAppView._View_MyApp_Host0.injectorGetInternal (MyApp.template.js:35:79)
at DebugAppView.AppView.injectorGet (http://192.168.56.1:8100/build/js/app.bundle.js:31753:21)
at DebugAppView.injectorGet (http://192.168.56.1:8100/build/js/app.bundle.js:31945:49)
at ElementInjector.get (http://192.168.56.1:8100/build/js/app.bundle.js:31246:33)
at ElementInjector.get (http://192.168.56.1:8100/build/js/app.bundle.js:31249:48)
7 298519 log DEVICE READY FIRED AFTER, 1004, ms
8 298609 log Entering: map-page
9 298620 log ERROR: , {}
This goes away when i restart the emulator but then sometimes appears again after more usage.
I thought this could be due to the SQLite db being created before the plugin is imported but i've imported the plugin in my root app.ts file (shown below) (the whole app can be seen in this github repo)
Root app.ts
#Component({
template: '<ion-nav [root]="rootPage"></ion-nav>',
// Declare services
providers: [ SqlService ]
})
export class MyApp {
rootPage: any = MapPage;
constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}
ionicBootstrap(MyApp);
I've confined any use of the plugin to an sqlService (shown below)
SQL Service
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
import { SQLite} from 'ionic-native';
#Injectable()
export class SqlService {
private db: SQLite;
private isOpen: boolean;
public constructor() {
if(!this.isOpen){
this.db = new SQLite();
this.db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
this.db.executeSql("CREATE TABLE IF NOT EXISTS places (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, img TEXT)", []).then((data) => {
console.log("TABLE CREATED: " + data);
}, (error) => {
console.log("Unable to execute SQL" + error);
});
this.isOpen=true;
});
}
}
public add(title: string, base64Img: string) {
return new Promise((resolve, reject) => {
this.db.executeSql("INSERT INTO places (title, img) VALUES ( '"+ title +"', '"+ base64Img +"')", [])
.then((data) => {
resolve(data);
}, (error) => {
reject(error);
});
});
}
// Remove individual Location
public remove(id: number){
return new Promise((resolve, reject) => {
this.db.executeSql("DELETE FROM places WHERE id = "+ id +";", [])
.then((data) => {
resolve(data);
}, (error) => {
reject(error);
});
});
}
public locDetail(id: number){
return new Promise((resolve, reject) => {
this.db.executeSql("SELECT * FROM places WHERE id = "+ id +";",[])
.then((data) => {
if(data.rows.length===1){
// console.log("ttitle: " + data.rows.item(0).title + " id: " + data.rows.item(0).id + " img: " + data.rows.item(0).img );
let place = [];
place.push({
id: data.rows.item(0).id,
title: data.rows.item(0).title,
img: data.rows.item(0).img
});
resolve(place);
}
}, (error) => {
reject(error);
});
});
}
// Refresh and initialise the places object
public refresh() {
return new Promise((resolve, reject) => {
this.db.executeSql("SELECT * FROM places", []).then((data) => {
let place = [];
if (data.rows.length > 0) {
for (var i = 0; i < data.rows.length; i++) {
place.push({
id: data.rows.item(i).id,
title: data.rows.item(i).title,
img: data.rows.item(i).img
});
}
}
resolve(place);
}, (error) => {
reject(error);
});
}
);
}
}
I recently had the same problem, make sure you check if the platform is ready also in your service. Import "Platform" from "ionic-angular" and execute your sqlite interaction code in the promise.then method.
I am using IOS and get the same error 'ReferenceError: sqlitePlugin is not defined'.
As the sqlite does not work when testing in Chrome browser I tried the ionic upload CLI function. Then got the same error.
I came across this issue on Github.
After using setTimeout it works in the Ionic view app on IOS:
setTimeout(function() {
let db = new SQLite();
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
db.executeSql("CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)", {}).then((data) => {
console.log("TABLE CREATED: ", data);
self.dummy = data
}, (error) => {
console.error("Unable to execute sql", error);
self.dummy = error;
})
}, (error) => {
console.error("Unable to open database", error);
self.dummy = error;
});
}, 2000);
please run your projeect in emulator/simulator or android device/ios device.it's not work on browser.