Skip to content

Commit

Permalink
tests: cover basic Meteor Methods and Publications
Browse files Browse the repository at this point in the history
  • Loading branch information
jankapunkt committed Feb 16, 2024
1 parent 63668bf commit b5b1f23
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 9 deletions.
6 changes: 3 additions & 3 deletions src/Call.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import Data from './Data';
* @param eventName {string} required, the method to call
* @param args {...array} optional arguments
*/
export default function (eventName) {
var args = Array.prototype.slice.call(arguments, 1);
export default function (eventName, ...args) {
let callback;
if (args.length && typeof args[args.length - 1] === 'function') {
var callback = args.pop();
callback = args.pop();
}

const id = Data.ddp.method(eventName, args);
Expand Down
6 changes: 3 additions & 3 deletions src/Meteor.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ const Meteor = {
const sub = Data.subscriptions[i];
idsMap.set(sub.subIdRemember, sub.id);
}

for (var i in message.subs) {
const subId = idsMap.get(message.subs[i]);
if (subId) {
Expand Down Expand Up @@ -342,8 +343,7 @@ const Meteor = {
Data.ddp.connect();
}
},
subscribe(name) {
let params = Array.prototype.slice.call(arguments, 1);
subscribe(name, ...params) {
let callbacks = {};
if (params.length) {
let lastParam = params[params.length - 1];
Expand Down Expand Up @@ -378,7 +378,7 @@ const Meteor = {
// being invalidated, we will require N matching subscribe calls to keep
// them all active.

let existing = false;
let existing = null;
for (let i in Data.subscriptions) {
const sub = Data.subscriptions[i];
if (sub.inactive && sub.name === name && EJSON.equals(sub.params, params))
Expand Down
87 changes: 85 additions & 2 deletions test/hooks/mockServer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { endpoint } from '../testHelpers';
import { Server } from 'mock-socket';
import EJSON from 'ejson';

let server;

Expand All @@ -11,7 +12,7 @@ module.exports = {
console.debug('Open mockserver on', endpoint);
server = new Server(endpoint);

// we need with never mock-socket versions to
// we need with newer mock-socket versions to
// handle the way it responds from within a connected callback
// thus we create some ioc pattern here to allow
// test clients to implement test-specific behaviour
Expand All @@ -26,14 +27,96 @@ module.exports = {
}
};

/**
* Test helper to define a Meteor Method
* @param name {string} name of the method
* @param response {function():*} function that mimics method behaviour
* @param log {function=} optional log function to log raw data
*/
server.method = ({ name, response, log = () => {} }) => {
server.message((data, server, socket) => {
log(name, data);
const parsed = EJSON.parse(data);
if (parsed.msg === 'method' && parsed.method === name) {
socket.send(
EJSON.stringify({
msg: 'result',
id: parsed.id,
result: response(...parsed.params),
})
);
}
});
};

/**
* Test helper to define a Meteor Publication. Supports subscribe/stop.
* @param name {string} name of the publication
* @param collection {string} name of the Mongo.Collection that will be synced by this pub
* @param getDocs {function():object|object[]} function that mimics the doc(s) to return from the pub
* @param log {function=} optional log function to log raw data
*/
server.publish = ({ name, collection, getDocs, log = () => {} }) => {
const messages = {
docs: {
sub: ({ doc, collection }) => {
const { _id, _version, ...fields } = doc;
return {
msg: 'added',
collection,
id: _id,
fields,
};
},
unsub: ({ doc, collection }) => {
return {
msg: 'removed',
collection,
id: doc._id,
};
},
},
state: {
sub: ({ id }) => {
return { msg: 'ready', subs: [id] };
},
unsub: ({ id }) => {
return { msg: 'nosub', id };
},
},
};
server.message((data, server, socket) => {
log(name, data);
const parsed = EJSON.parse(data);
if (['sub', 'unsub'].includes(parsed.msg)) {
let docs = getDocs(...(parsed.params || []));
docs = Array.isArray(docs) ? docs : [docs];
docs.forEach((doc) => {
const docMessage = messages.docs[parsed.msg]({
doc,
collection,
...parsed,
});
setTimeout(() => socket.send(EJSON.stringify(docMessage)), 1);
});

const stateMessage = messages.state[parsed.msg]({
collection,
...parsed,
});
setTimeout(() => socket.send(EJSON.stringify(stateMessage), 20));
}
});
};

server.on('connection', (socket) => {
socket.on('message', (data) => {
currentMessageFn(data, server, socket);
});

// simulate that we got a successful connection
setTimeout(() => {
socket.send(JSON.stringify({ msg: 'connected' }));
socket.send(EJSON.stringify({ msg: 'connected' }));
}, 10);
});
},
Expand Down
70 changes: 69 additions & 1 deletion test/src/Meteor.tests.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import { expect } from 'chai';
import Meteor from '../../src/Meteor';
import Mongo from '../../src/Mongo';
import { awaitDisconnected, stub, restoreAll } from '../testHelpers';
import DDP from '../../lib/ddp';
import { endpoint } from '../testHelpers';
import { server } from '../hooks/mockServer';
import Random from '../../lib/Random';
import Data from '../../src/Data';

Meteor.enableVerbose();

describe('Meteor - integration', function () {
beforeEach(awaitDisconnected);

it('uses the default async storage if none is defined', function () {
const fallback =
require('@react-native-async-storage/async-storage').default;
const { AsyncStorage } = Meteor.packageInterface();
expect(AsyncStorage).to.equal(fallback);
});

describe('deprecated', () => {
it('throws if Meteor.collection is used', () => {
expect(() => new Meteor.collection()).to.throw(
'Meteor.collection is deprecated. Use Mongo.Collection'
);
});
});

describe(Meteor.connect.name, () => {
beforeEach(awaitDisconnected);
afterEach(() => {
restoreAll();
});
Expand Down Expand Up @@ -57,4 +72,57 @@ describe('Meteor - integration', function () {
});
});
});
describe('call', () => {
it('calls a server Method', (done) => {
Meteor.connect(endpoint, { NetInfo: null });

server().method({ name: 'foo', response: () => 'bar' });

Meteor.getData().waitDdpConnected(() => {
Meteor.call('foo'); // silent response
Meteor.call('foo', (err, res) => {
expect(err).to.equal(undefined);
expect(res).to.equal('bar');
done();
});
});
});
});
describe(Meteor.subscribe.name, () => {
it('subscribes to a server publication', (done) => {
Meteor.connect(endpoint, { NetInfo: null, autoConnect: false });
const collection = new Mongo.Collection('foobarSubs');
const _id = Random.id();

server().publish({
name: 'bar',
collection: 'foobarSubs',
getDocs: () => ({ _id, foo: 'bar' }),
});

let readyCalled = false;
Meteor.getData().waitDdpConnected(() => {
setTimeout(() => {
const handle = Meteor.subscribe('bar', {
onReady: () => {
expect(collection.find({}).fetch()).to.deep.equal([
{ _id, foo: 'bar', _version: 1 },
]);
readyCalled = true;
handle.stop();
},
onStop: () => {
setTimeout(() => {
// doc should have been removed
expect(collection.find({}).fetch()).to.deep.equal([]);
done();
}, 200);
},
});
}, 100);
});

Meteor.getData().ddp.connect();
});
});
});
11 changes: 11 additions & 0 deletions test/testHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,14 @@ export const awaitDisconnected = async () => {
}, 100);
});
};

// credits: https://stackoverflow.com/a/30158566/3098783
export const props = (obj) => {
let p = [];
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
let op = Object.getOwnPropertyNames(obj);
for (let i = 0; i < op.length; i++)
if (p.indexOf(op[i]) === -1) p.push(op[i]);
}
return p;
};

0 comments on commit b5b1f23

Please sign in to comment.