diff --git a/client/lib/tests/Utils.tests.js b/client/lib/tests/Utils.tests.js new file mode 100644 index 000000000..e954fcd2d --- /dev/null +++ b/client/lib/tests/Utils.tests.js @@ -0,0 +1,101 @@ +/* eslint-env mocha */ +import sinon from 'sinon'; +import { expect } from 'chai'; +import { Random } from 'meteor/random'; +import '../utils'; + + +describe('Utils', function() { + beforeEach(function() { + sinon.stub(Utils, 'reload').callsFake(() => {}); + }); + + afterEach(function() { + window.localStorage.removeItem(boardView); + sinon.restore(); + }); + + const boardView = 'boardView'; + + describe(Utils.setBoardView.name, function() { + it('sets the board view if the user exists', function(done) { + const viewId = Random.id(); + const user = { + setBoardView: (view) => { + expect(view).to.equal(viewId); + done(); + }, + }; + sinon.stub(Meteor, 'user').callsFake(() => user); + Utils.setBoardView(viewId); + + expect(window.localStorage.getItem(boardView)).to.equal(viewId); + }); + + it('sets a specific view if no user exists but a view is defined', function() { + const views = [ + 'board-view-swimlanes', + 'board-view-lists', + 'board-view-cal' + ]; + + sinon.stub(Meteor, 'user').callsFake(() => {}); + + views.forEach(viewName => { + Utils.setBoardView(viewName); + expect(window.localStorage.getItem(boardView)).to.equal(viewName); + }); + }); + + it('sets a default view if no user and no view are given', function() { + sinon.stub(Meteor, 'user').callsFake(() => {}); + Utils.setBoardView(); + expect(window.localStorage.getItem(boardView)).to.equal('board-view-swimlanes'); + }); + }); + + describe(Utils.unsetBoardView.name, function() { + it('removes the boardview from localStoage', function() { + window.localStorage.setItem(boardView, Random.id()); + window.localStorage.setItem('collapseSwimlane', Random.id()); + + Utils.unsetBoardView(); + + expect(window.localStorage.getItem(boardView)).to.equal(null); + expect(window.localStorage.getItem('collapseSwimlane')).to.equal(null); + }); + }); + + describe(Utils.boardView.name, function() { + it('returns the user\'s board view if a user exists', function() { + const viewId = Random.id(); + const user = {}; + sinon.stub(Meteor, 'user').callsFake(() => user); + expect(Utils.boardView()).to.equal(undefined); + + const boardView = Random.id(); + user.profile = { boardView }; + + expect(Utils.boardView()).to.equal(boardView); + }); + it('returns the current defined view', function() { + const views = [ + 'board-view-swimlanes', + 'board-view-lists', + 'board-view-cal' + ]; + + sinon.stub(Meteor, 'user').callsFake(() => {}); + + views.forEach(viewName => { + window.localStorage.setItem(boardView, viewName); + expect(Utils.boardView()).to.equal(viewName); + }); + }); + it('returns a default if nothing is set', function() { + sinon.stub(Meteor, 'user').callsFake(() => {}); + expect(Utils.boardView()).to.equal('board-view-swimlanes'); + expect(window.localStorage.getItem(boardView)).to.equal('board-view-swimlanes'); + }); + }); +}); diff --git a/client/lib/tests/index.js b/client/lib/tests/index.js new file mode 100644 index 000000000..23051aa8c --- /dev/null +++ b/client/lib/tests/index.js @@ -0,0 +1 @@ +import './Utils.tests'; diff --git a/client/lib/utils.js b/client/lib/utils.js index e91d08380..f8f684325 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -1,20 +1,27 @@ Utils = { + reload () { + // we move all window.location.reload calls into this function + // so we can disable it when running tests. + // This is because we are not allowed to override location.reload but + // we can override Utils.reload to prevent reload during tests. + window.location.reload(); + }, setBoardView(view) { currentUser = Meteor.user(); if (currentUser) { Meteor.user().setBoardView(view); } else if (view === 'board-view-swimlanes') { window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true - location.reload(); + Utils.reload(); } else if (view === 'board-view-lists') { window.localStorage.setItem('boardView', 'board-view-lists'); //true - location.reload(); + Utils.reload(); } else if (view === 'board-view-cal') { window.localStorage.setItem('boardView', 'board-view-cal'); //true - location.reload(); + Utils.reload(); } else { window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true - location.reload(); + Utils.reload(); } }, @@ -39,7 +46,7 @@ Utils = { return 'board-view-cal'; } else { window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true - location.reload(); + Utils.reload(); return 'board-view-swimlanes'; } }, @@ -64,7 +71,7 @@ Utils = { setMyCardsSort(sort) { window.localStorage.setItem('myCardsSort', sort); - location.reload(); + Utils.reload(); }, archivedBoardIds() { @@ -87,7 +94,7 @@ Utils = { setDueCardsView(view) { window.localStorage.setItem('dueCardsView', view); - location.reload(); + Utils.reload(); }, // XXX We should remove these two methods diff --git a/server/lib/utils.tests.js b/server/lib/utils.tests.js new file mode 100644 index 000000000..3ad629d9a --- /dev/null +++ b/server/lib/utils.tests.js @@ -0,0 +1,106 @@ +/* eslint-env mocha */ +import { Random } from 'meteor/random'; +import { expect } from 'chai'; +import './utils'; + +describe('utils', function() { + describe(allowIsBoardAdmin.name, function() { + it('returns if a board has an admin', function() { + const userId = Random.id(); + const board = { + hasAdmin: id => { + return id === userId; + } + }; + + expect(allowIsBoardAdmin(userId, board)).to.equal(true); + expect(allowIsBoardAdmin(Random.id(), board)).to.equal(false); + }); + }); + + describe(allowIsBoardMember.name, function() { + it('returns if a board has a member', function() { + const userId = Random.id(); + const board = { + hasMember: id => { + return id === userId; + } + }; + + expect(allowIsBoardMember(userId, board)).to.equal(true); + expect(allowIsBoardMember(Random.id(), board)).to.equal(false); + }); + }); + + describe(allowIsAnyBoardMember.name, function() { + it('returns if any board has a member', function() { + const userId = Random.id(); + const boardsExpectedTrue = [{ + hasMember: id => { + return id === userId; + } + }]; + + expect(allowIsAnyBoardMember(userId, boardsExpectedTrue)).to.equal(true); + expect(allowIsAnyBoardMember(Random.id(), boardsExpectedTrue)).to.equal(false); + + const boardsExpectedFalse = [{ + hasMember: () => false + }]; + + expect(allowIsAnyBoardMember(userId, boardsExpectedFalse)).to.equal(false); + expect(allowIsAnyBoardMember(Random.id(), boardsExpectedFalse)).to.equal(false); + }); + }); + + describe(allowIsBoardMemberCommentOnly.name, function() { + it('returns if a board has a member that is not comment-only member', function() { + const userId = Random.id(); + const board = { + hasMember: id => { + return id === userId; + }, + hasCommentOnly: id => { + return id !== userId; + } + }; + + expect(allowIsBoardMemberCommentOnly(userId, board)).to.equal(true); + expect(allowIsBoardMemberCommentOnly(Random.id(), board)).to.equal(false); + }); + }); + + describe(allowIsBoardMemberNoComments.name, function() { + it('returns if a board has a member that has comment any comments', function() { + const userId = Random.id(); + const board = { + hasMember: id => { + return id === userId; + }, + hasNoComments: id => { + return id !== userId; + } + }; + + expect(allowIsBoardMemberNoComments(userId, board)).to.equal(true); + expect(allowIsBoardMemberNoComments(Random.id(), board)).to.equal(false); + }); + }); + + describe(allowIsBoardMemberByCard.name, function() { + it('returns if the board for a given card has a member', function() { + const userId = Random.id(); + const board = { + hasMember: id => { + return id === userId; + } + }; + const card = { + board: () => board + }; + + expect(allowIsBoardMemberByCard(userId, card)).to.equal(true); + expect(allowIsBoardMemberByCard(Random.id(), card)).to.equal(false); + }); + }); +}); diff --git a/tests/main.js b/tests/main.js new file mode 100644 index 000000000..c7c7a28b3 --- /dev/null +++ b/tests/main.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ + +// This is the main test file from which all tests can be imported top-down, +// creating a directed sequence for tests that sums up to our test-suite. +// +// You propably want to start with low-level code and follow up to higher-level +// code, like for example: +// +// infrastructure +// utils / helpers +// contexts +// api +// components +// ui + +// If you want to run tests on both, server AND client, simply import them as +// they are. However, if you want to restict tests to server-only or client-only +// you need to wrap them inside a new describe-block + +if (Meteor.isServer) { + describe('server', function() { + import '../server/lib/utils.tests'; + }); +} + +if (Meteor.isClient) { + describe('lib', function() { + import '../client/lib/tests'; + }); +}