Skip to content

Commit

Permalink
More cleanup + tests are passing locally
Browse files Browse the repository at this point in the history
  • Loading branch information
wreiske committed Oct 15, 2019
1 parent 913fc7a commit 5581e5e
Show file tree
Hide file tree
Showing 16 changed files with 270 additions and 278 deletions.
17 changes: 6 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
user-status [![Build Status](https://travis-ci.org/mizzao/meteor-user-status.png?branch=master)](https://travis-ci.org/mizzao/meteor-user-status)
user-status [![Build Status](https://travis-ci.org/Meteor-Community-Packages/meteor-user-status.png?branch=master)](https://travis-ci.org/Meteor-Community-Packages/meteor-user-status)
===========

## What's this do?
Expand All @@ -9,17 +9,12 @@ some other objects. This allows you to easily see users that are online, for
applications such as rendering the users box below showing online users in green
and idle users in orange.

![User online states](https://raw.github.com/mizzao/meteor-user-status/master/docs/example.png)
![User online states](https://raw.github.com/Meteor-Community-Packages/meteor-user-status/master/docs/example.png)

For a complete example of what can be tracked, including inactivity, IP
addresses, and user agents, check out a demo app at
http://user-status.meteor.com, or its
[source](https://github.com/mizzao/meteor-user-status/tree/master/demo).

Help keep your favorite Meteor packages alive! If you depend on this package in
your app and find it useful, consider a donation at
[Gittip](https://www.gittip.com/mizzao/) for me (or other Meteor package
maintainers).
[source](https://github.com/Meteor-Community-Packages/meteor-user-status/tree/master/demo).

## Install

Expand All @@ -37,14 +32,14 @@ for more details.

## Basic Usage - Online State

This package maintains two types of status: a general user online flag in `Meteor.users`, and some additional data for each session. It uses [timesync](https://github.com/mizzao/meteor-timesync) to maintain the server's time across all clients, regardless of whether they have the correct time.
This package maintains two types of status: a general user online flag in `Meteor.users`, and some additional data for each session. It uses [timesync](https://github.com/Meteor-Community-Packages/meteor-timesync) to maintain the server's time across all clients, regardless of whether they have the correct time.

`Meteor.users` receives a `status` field will be updated automatically if the user logs in or logs out, closes their browser, or otherwise disconnects. A user is online if at least one connection with that `userId` is logged in. It contains the following fields:

- `online`: `true` if there is at least one connection online for this user
- `lastLogin`: information about the most recent login of the user, with the fields `date`, `ipAddr`, and `userAgent`.
- `idle`: `true` if all connections for this user are idle. Requires idle tracking to be turned on for all connections, as below.
- `lastActivity`: if the user was idle, the last time an action was observed. This field is only available when the user is online and idle. It does not maintain the user's last activity in real time or a stored value indefinitely - `lastLogin` is a coarse approximation to that. For more information, see https://github.com/mizzao/meteor-user-status/issues/80.
- `lastActivity`: if the user was idle, the last time an action was observed. This field is only available when the user is online and idle. It does not maintain the user's last activity in real time or a stored value indefinitely - `lastLogin` is a coarse approximation to that. For more information, see https://github.com/Meteor-Community-Packages/meteor-user-status/issues/80.

To make this available on the client, use a reactive cursor, such as by creating a publication on the server:

Expand Down Expand Up @@ -105,7 +100,7 @@ On the client, the `UserStatus` object provides for seamless automatic monitorin
- `pingMonitor`: if the automatic event handlers aren't catching what you need, you can manually ping the monitor to signal that a user is doing something and reset the idle monitor.
- `isIdle`: a reactive variable signifying whether the user is currently idle or not.
- `isMonitoring`: a reactive variable for whether the monitor is running.
- `lastActivity`: a reactive variable for the last action recorded by the user (according to [server time](https://github.com/mizzao/meteor-timesync)). Since this variable will be invalidated a lot and cause many recomputations, it's best only used for debugging or diagnostics (as in the demo).
- `lastActivity`: a reactive variable for the last action recorded by the user (according to [server time](https://github.com/Meteor-Community-Packages/meteor-timesync)). Since this variable will be invalidated a lot and cause many recomputations, it's best only used for debugging or diagnostics (as in the demo).

For an example of how the above functions are used, see the demo.

Expand Down
2 changes: 1 addition & 1 deletion monitor.js → client/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const activityDep = new Tracker.Dependency;
let focused = true;

// These settings are internal or exported for test only
var MonitorInternals = {
export let MonitorInternals = {
idleThreshold: null,
idleOnBlur: false,

Expand Down
1 change: 1 addition & 0 deletions demo/packages/meteor-user-status/client/monitor.js
1 change: 0 additions & 1 deletion demo/packages/meteor-user-status/monitor.js

This file was deleted.

1 change: 1 addition & 0 deletions demo/packages/meteor-user-status/server/status.js
1 change: 0 additions & 1 deletion demo/packages/meteor-user-status/status.js

This file was deleted.

7 changes: 4 additions & 3 deletions package.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Package.onUse((api) => {
testOnly: true
});

api.mainModule('monitor.js', 'client');
api.mainModule('status.js', 'server');
api.mainModule('client/monitor.js', 'client');
api.mainModule('server/status.js', 'server');

});

Expand All @@ -49,5 +49,6 @@ Package.onTest((api) => {
api.addFiles('tests/monitor_tests.js', 'client');
api.addFiles('tests/status_tests.js', 'server');

api.addFiles('tests/server_client_tests.js');
api.addFiles('tests/server_tests.js', 'server');
api.addFiles('tests/client_tests.js', 'client');
});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"homepage": "https://github.com/Meteor-Community-Packages/meteor-user-status#readme",
"private": false,
"scripts": {
"lint": "./node_modules/.bin/eslint ."
"lint": "./node_modules/.bin/eslint .",
"test": "meteor test-packages --once --driver-package test-in-console"
},
"devDependencies": {
"autoprefixer": "^9.6.1",
Expand Down
8 changes: 4 additions & 4 deletions status.js → server/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import { Accounts } from 'meteor/accounts-base';
import { check, Match } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { _ } from 'underscore';
import { _ } from 'meteor/underscore';
import { EventEmitter } from 'events';

const UserConnections = new Mongo.Collection('user_status_sessions', {
connection: null
});

// eslint-disable-next-line no-undef
const statusEvents = new(Npm.require('events').EventEmitter)();
const statusEvents = new(EventEmitter)();

/*
Multiplex login/logout events to status.online
Expand Down Expand Up @@ -261,7 +261,7 @@ Accounts.onLogin(info => loginSession(info.connection, new Date(), info.user._id

// pub/sub trick as referenced in http://stackoverflow.com/q/10257958/586086
// We used this in the past, but still need this to detect logouts on the same connection.
Meteor.publish(null, () => {
Meteor.publish(null, function () {
// Return null explicitly if this._session is not available, i.e.:
// https://github.com/arunoda/meteor-fast-render/issues/41
if (this._session == null) {
Expand Down
192 changes: 192 additions & 0 deletions tests/client_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/* globals navigator, Package, Tinytest */

import { Meteor } from 'meteor/meteor';
import { TimeSync } from 'meteor/mizzao:timesync';
import { InsecureLogin } from './insecure_login';

// The maximum tolerance we expect in client-server tests
// TODO why must this be so large?
const timeTol = 1000;
let loginTime = null;
let idleTime = null;

// Monitor tests will wait for timesync, so we don't need to here.
Tinytest.addAsync('status - login', (test, next) => InsecureLogin.ready(() => {
test.ok();
loginTime = new Date(TimeSync.serverTime());
return next();
}));

// Check that initialization is empty
Tinytest.addAsync('status - online recorded on server', (test, next) => Meteor.call('grabStatus', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const user = res[0];
test.equal(user._id, Meteor.userId());
test.equal(user.status.online, true);

test.isTrue(Math.abs(user.status.lastLogin.date - loginTime) < timeTol);

// TODO: user-agent doesn't seem to match up in phantomjs for some reason
if (Package['test-in-console'] == null) {
test.equal(user.status.lastLogin.userAgent, navigator.userAgent);
}

test.equal(user.status.idle, false);
test.isFalse(user.status.lastActivity != null);

return next();
}));

Tinytest.addAsync('status - session recorded on server', (test, next) => Meteor.call('grabSessions', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const doc = res[0];
test.equal(doc.userId, Meteor.userId());
test.isTrue(doc.ipAddr != null);
test.isTrue(Math.abs(doc.loginTime - loginTime) < timeTol);

// This shit doesn't seem to work properly in PhantomJS on Travis
if (Package['test-in-console'] == null) {
test.equal(doc.userAgent, navigator.userAgent);
}

test.isFalse(doc.idle != null); // connection record, not user
test.isFalse(doc.lastActivity != null);

return next();
}));

Tinytest.addAsync('status - online recorded on client', (test, next) => {
test.equal(Meteor.user().status.online, true);
return next();
});

Tinytest.addAsync('status - idle report to server', (test, next) => {
const now = TimeSync.serverTime();
idleTime = new Date(now);

return Meteor.call('user-status-idle', now, (err) => {
test.isUndefined(err);

// Testing grabStatus should be sufficient to ensure that sessions work
return Meteor.call('grabStatus', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const user = res[0];
test.equal(user._id, Meteor.userId());
test.equal(user.status.online, true);
test.equal(user.status.idle, true);
test.isTrue(user.status.lastLogin != null);
// This should be the exact date we sent to the server
test.equal(user.status.lastActivity, idleTime);

return next();
});
});
});

Tinytest.addAsync('status - active report to server', (test, next) => {
const now = TimeSync.serverTime();

return Meteor.call('user-status-active', now, (err) => {
test.isUndefined(err);

return Meteor.call('grabStatus', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const user = res[0];
test.equal(user._id, Meteor.userId());
test.equal(user.status.online, true);
test.isTrue(user.status.lastLogin != null);

test.equal(user.status.idle, false);
test.isFalse(user.status.lastActivity != null);

return next();
});
});
});

Tinytest.addAsync('status - idle report with no timestamp', (test, next) => {
const now = TimeSync.serverTime();
idleTime = new Date(now);

return Meteor.call('user-status-idle', undefined, (err) => {
test.isUndefined(err);

return Meteor.call('grabStatus', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const user = res[0];
test.equal(user._id, Meteor.userId());
test.equal(user.status.online, true);
test.equal(user.status.idle, true);
test.isTrue(user.status.lastLogin != null);
// This will be approximate
test.isTrue(Math.abs(user.status.lastActivity - idleTime) < timeTol);

return next();
});
});
});

Tinytest.addAsync('status - active report with no timestamp', (test, next) => Meteor.call('user-status-active', undefined, (err) => {
test.isUndefined(err);

return Meteor.call('grabStatus', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const user = res[0];
test.equal(user._id, Meteor.userId());
test.equal(user.status.online, true);
test.isTrue(user.status.lastLogin != null);

test.equal(user.status.idle, false);
test.isFalse(user.status.lastActivity != null);

return next();
});
}));

Tinytest.addAsync('status - logout', (test, next) => Meteor.logout((err) => {
test.isUndefined(err);
return next();
}));

Tinytest.addAsync('status - offline recorded on server', (test, next) => Meteor.call('grabStatus', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const user = res[0];
test.isTrue(user._id != null);
test.equal(user.status.online, false);
// logintime is still maintained
test.isTrue(user.status.lastLogin != null);

test.isFalse(user.status.idle != null);
test.isFalse(user.status.lastActivity != null);

return next();
}));

Tinytest.addAsync('status - session userId deleted on server', (test, next) => Meteor.call('grabSessions', function (err, res) {
test.isUndefined(err);
test.length(res, 1);

const doc = res[0];
test.isFalse(doc.userId != null);
test.isTrue(doc.ipAddr != null);
test.isFalse(doc.loginTime != null);

test.isFalse(doc.idle); // === false
test.isFalse(doc.lastActivity != null);

return next();
}));
4 changes: 1 addition & 3 deletions tests/insecure_login.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Accounts } from 'meteor/accounts-base';
import { Meteor } from 'meteor/meteor';
import { _ } from 'underscore';

/*
* Created by https://github.com/matb33 for testing packages that make user of userIds
* Original file https://github.com/matb33/meteor-collection-hooks/blob/master/tests/insecure_login.js
*/

export const InsecureLogin = {
queue: [],
ran: false,
Expand All @@ -19,7 +17,7 @@ export const InsecureLogin = {
InsecureLogin.unwind();
},
unwind: () => {
_.each(InsecureLogin.queue, (callback) => {
InsecureLogin.queue.forEach((callback) => {
callback();
});
InsecureLogin.queue = [];
Expand Down
2 changes: 1 addition & 1 deletion tests/monitor_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Meteor } from 'meteor/meteor';
import { TimeSync } from 'meteor/mizzao:timesync';
import { Tracker } from 'meteor/tracker';
import { MonitorInternals, UserStatus } from '../status';
import { MonitorInternals, UserStatus } from '../client/monitor';
import { getCleanupWrapper } from './setup';

const tolMs = 100;
Expand Down
Loading

0 comments on commit 5581e5e

Please sign in to comment.