Support for async/await in Meteor tests - meteor

I can't seem to be able to test a meteor method when it's written using async/await.
server/methods.js
Meteor.methods({
async f() {
const result = await new Promise((resolve) =>
setTimeout(() => resolve("i'm done"), 500));
return result;
}
})
server/methods.test.js
import {Meteor} from 'meteor/meteor';
import "./methods";
describe("f", () => {
it("should return _i'm done_", done => {
Meteor.call("f", (err, res) => {
// MY PROBLEM: res equals {}
done(err);
});
});
});
There is nothing special about my setup. This is the command I use to start my tests, meteor version is 1.4.2
meteor test --driver-package dispatch:mocha

Related

Jest useFakeTimers() blocks when used with firebase emulators

I'm trying to use jest.useFakeTimers() to make my firebase + jest tests stable when working with dates and timeouts. However, if I try to read anything from the emulated database the test times out and throws Exceeded timeout of 5000 ms for a test. Using jest.setTimeout(30000) doesn't help.
Github repro: https://github.com/vojdan/jest-fake-timers-firebase
Here's the test:
import * as admin from "firebase-admin";
// jest.setTimeout(30000);
describe("emulated firebase test", () => {
beforeAll(() => {
jest.useFakeTimers();
jest.setSystemTime(1349852318000);
admin.initializeApp({
projectId: "test-project",
databaseURL: "http://localhost:9000?ns=test-project",
});
});
afterAll(() => {
jest.useRealTimers();
});
it("only passes with fake timers", () => {
expect.assertions(1);
expect(new Date().valueOf()).toBe(1349852318000);
});
it("hangs when fake timers are used", async () => {
expect.assertions(1);
try {
const firestore = admin.firestore().doc("anything/really");
const realtime = admin.database().ref("anyhting/really");
console.log("this runs:", new Date().valueOf());
// ----- EVERYTHING RUNS UP TO HERE -----
// ----- any of the following commands will block when used with jest.useFakeTimers(); -----
// await firestore.get();
const ref = await realtime.once("value");
const value = ref.val();
console.log("this doesn't run:", value);
expect(1).toBe(1);
} catch (error) {
console.log("THERE WAS AN ERROR:", error);
}
});
});
Since useFakeTimers isn't working for me, I'm using this as a workaround:
jest.mock('date-fns', () => {
// Require the original module to not be mocked...
const originalModule = jest.requireActual('date-fns');
return {
...originalModule,
getTime: jest.fn(() => mockedTimestamp),
getUnixTime: jest.fn(() => mockedTimestampUnix),
format: jest.fn(input => 'mockedTimeString'),
};
});

How to mock firebase firestore using jest with #react-native-firebase/firestore

I'm new to testing and I'm trying to fake a document update call using Jest. To do so I'm using react-native-testing-library and react-native-firebase.
Below is my test suite. I have one input, and when I'm changing its value the document should be updated
import firestore from "#react-native-firebase/firestore"
jest.mock("#react-native-firebase/firestore", () => {
return () => ({
collection: jest.fn(() => ({
doc: jest.fn(() => ({
update: jest.fn(),
})),
})),
});
});
describe("<EditInfosStudy>", () => {
const willSave = true;
const setDidChanged = jest.fn();
test("will update", async () => {
const { getByTestId } = render(
<EditInfosStudy willSave={willSave} setDidChanged={setDidChanged} />
);
const input = getByTestId("EditInfosStudy_TextInput");
fireEvent.changeText(input, "study text");
expect(input.props.value).toBe("study text");
// expect(firestore().collection('collection').doc('doc').update({})).toHaveBeenCalled()
// this is where i'm stuck
});
});
After Running the test Jest indicate a console.log from the function (below) to confirm that the document was updated and the test passes.
What I don't understand is how do I check if the document were updated on the last line using the expect method expect(firestore()ā€¦) Iā€™m really confused on the whole mocking part, any help would be greatly appreciated!
belove is the function being called :
if (localChange && willSave) {
(async () => {
try {
await firestore()
.collection("users")
.doc(user.uid)
.update({ study });
setUser({ study });
console.log("study updated");
} catch (err) {
console.error(err);
}
})();
}

Sequelize in-memory sqlite tests isolated db?

I have the following tests where I am trying to exercise my db model against a sqlite in-memory database
describe(`task model`, () => {
let Task, sandbox;
beforeEach(() => (sandbox = createSandbox()));
afterEach(() => sandbox.restore());
beforeEach(async () => {
const dbConnection = new Sequelize(`sqlite::memory:`, { logging: false });
stub(dbConnectionModule, `dbConnection`).value(dbConnection);
Task = require(`./models/task.js`).Task;
await dbConnection.sync({ force: true });
});
describe(`create a task`, () => {
let task;
beforeEach(async () => {
task = Task.build(taskData);
await task.save();
});
it(`gets an id`, () => expect(task.id).to.not.be.empty);
it(`shows up with a find all`, async () => {
const tasks = await Task.findAll();
expect(tasks).to.have.length(1);
});
});
});
I can verify the connection was newed up twice and yet my second test confusingly comes back with 2 tasks created.
It seems to be using the same db as on the first test!
I was under the impression that I would get a new in-memory db each time the Sequelize constructor is called for sqlite's in-memory db, am I misunderstanding something?
Alternately, how do I drop and recreate the database entirely for a new test?
Surprisingly, even the following after the sync doesn't seem to work
for (const model of Object.values(dbConnection.models))
model.destroy({ truncate: true });
Hihi,
What I did was to replace the database in the index file, not in the tests.
models/index.js
...
const sequelize = require('sequelize')
let sequelize;
if(process.env.NODE_ENV === 'test') {
sequelize = new Sequelize('sqlite::memory:');
}
...
and in your tests just import as usual
const { Task } = require('./models/index.js');
describe(`task model`, () => {
beforeEach(() => (sandbox = createSandbox()));
describe(`create a task`, () => {
let task;
beforeEach(async () => {
task = Task.build(taskData);
await task.save();
});
it(`gets an id`, () => expect(task.id).to.not.be.empty);
it(`shows up with a find all`, async () => {
const tasks = await Task.findAll();
expect(tasks).to.have.length(1);
});
});
});
You must/should implement a solution to init the database in every test you do.

Cloud firebase batch update

I am trying to use a batch update to update a count within each snapshot. But it seems like the function doesnt even run. I know it has something to do with the second promise but I am not sure where.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
export const replyCreated = functions
.firestore
.document(`/Reply/{replyId}`)
.onCreate((change: any, context: functions.EventContext) => {
const promises = [];
promises.push(admin.firestore().doc(`Challenge/${change.data().challenge_id}`).update({replyCount: admin.firestore.FieldValue.increment(1)}))
promises.push(admin.firestore()
.collection(`User`)
.where('following', 'array-contains', change.data().user_id).get().then((snapshot: any) => {
if (!snapshot.empty) {
const batch = admin.firestore().batch();
snapshot.forEach((doc: any) => {
const tempObject = doc.data()
console.log(`/Subscribed_Challenges/${tempObject.userId}/myChallenges/${change.data().challenge_id}`)
const myChallenge = admin.firestore().doc(`/Subscribed_Challenges/${tempObject.userId}/myChallenges/${change.data().challenge_id}`)
batch.update(myChallenge, {replyCount: admin.firestore.FieldValue.increment(1)})
})
return batch.commit().catch((err: any) => {
console.log('Batch Error', err)
});
}
else {
return Promise.resolve()
}
}))
return Promise.all(promises)
.then(() => {
return "upvote complete";
})
})
If I correctly understand you code, you don't need to use Promise.all() but you need to correctly chain the different Promises returned by the asynchronous Firestore methods.
The following should do the trick (untested):
export const replyCreated = functions
.firestore
.document(`/Reply/{replyId}`)
.onCreate((change: any, context: functions.EventContext) => {
return admin.firestore().doc(`Challenge/${change.data().challenge_id}`).update({ replyCount: admin.firestore.FieldValue.increment(1) })
.then(() => {
return admin.firestore()
.collection(`User`)
.where('following', 'array-contains', change.data().user_id).get()
})
.then((snapshot: any) => {
if (!snapshot.empty) {
const batch = admin.firestore().batch();
snapshot.forEach((doc: any) => {
const tempObject = doc.data()
console.log(`/Subscribed_Challenges/${tempObject.userId}/myChallenges/${change.data().challenge_id}`)
const myChallenge = admin.firestore().doc(`/Subscribed_Challenges/${tempObject.userId}/myChallenges/${change.data().challenge_id}`)
batch.update(myChallenge, { replyCount: admin.firestore.FieldValue.increment(1) })
})
return batch.commit()
}
else {
throw new Error('Snapshot empty')
}
})
.catch((err: any) => {
console.log('Error', err);
return null;
});
})
You would use Promise.all() if you need to execute a number of asynchronous methods (which return a Promise) in parallel. In your case (if I am not mistaking) the only case where you need to execute asynchronous methods in parallel is in the block where you use the batched write, therefore the parallel execution is executed by the batched write itself. For the other methods, it is more a sequential execution and you have to chain the promises with the then() method.

Angular 2 AND Karma Test AND Async HTTP

so my problem is very easy to explain
this is my test spec
import {
describe,
expect,
it,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {RestClient} from './rest.service';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/delay';
import {
HTTP_PROVIDERS
} from 'angular2/http';
export function main() {
describe('RestClient Service', () => {
beforeEachProviders( () => [HTTP_PROVIDERS, RestClient] );
it('is defined', inject( [RestClient], (client) =>{
client.get('http://jsonplaceholder.typicode.com/posts/1')
.delay(2000)
.toPromise()
.then((res) => {
console.log('test');
expect(res.length).toBeGreaterThan(1000);
});
}));
});
}
and this is the method in the "RestClient" class that return an Observable
public get(url:string): Observable<any> {
return this.http.get(url).map(res => res.json());
}
So, i start the test and the test return
START:
LOG: 'ciao'
RestClient Service
āœ” is defined
PhantomJS 2.0.0 (Mac OS X 0.0.0) LOG: 'ciao'
Finished in 0.026 secs / 0.038 secs
SUMMARY:
āœ” 2 tests completed
For Karma all work well and the test is passed correctly and is not true, and at the same time if i put a console.log into the "then" never is called.
Som i suppose that is a problem with Async calls, do you have any idea howto test in Angular2 Async Calls
i have used Inject and AsyncInject too.
I know that i can use a MockBackend but i need to test with external urls
thanks in advance for your help
injectAsync should solve your problem, but you need to return the promise:
it('is defined', injectAsync( [RestClient], (client) =>{
return client.get('http://jsonplaceholder.typicode.com/posts/1')
.delay(2000)
.toPromise()
.then((res) => {
console.log('test');
expect(res.length).toBeGreaterThan(1000);
});
}));

Resources