mirror of https://github.com/wekan/wekan
commit
aa59007409
@ -1,5 +1,12 @@ |
||||
{ |
||||
"presets": [ |
||||
"@babel/preset-stage-3" |
||||
] |
||||
], |
||||
"env": { |
||||
"COVERAGE": { |
||||
"plugins": [ |
||||
"istanbul" |
||||
] |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,161 @@ |
||||
name: Test suite |
||||
|
||||
on: |
||||
push: |
||||
branches: |
||||
- master |
||||
- develop |
||||
pull_request: |
||||
|
||||
jobs: |
||||
# the following are optional jobs and need to be configured according |
||||
# to this project's settings: |
||||
# |
||||
# lintcode: |
||||
# name: Javascript lint |
||||
# runs-on: ubuntu-latest |
||||
# steps: |
||||
# - name: checkout |
||||
# uses: actions/checkout@v2 |
||||
# |
||||
# - name: setup node |
||||
# uses: actions/setup-node@v1 |
||||
# with: |
||||
# node-version: '12.x' |
||||
# |
||||
# - name: cache dependencies |
||||
# uses: actions/cache@v1 |
||||
# with: |
||||
# path: ~/.npm |
||||
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} |
||||
# restore-keys: | |
||||
# ${{ runner.os }}-node- |
||||
# |
||||
# - run: npm install |
||||
# - run: npm run lint:code |
||||
# |
||||
# lintstyle: |
||||
# name: SCSS lint |
||||
# runs-on: ubuntu-latest |
||||
# needs: [lintcode] |
||||
# steps: |
||||
# - name: checkout |
||||
# uses: actions/checkout@v2 |
||||
# |
||||
# - name: setup node |
||||
# uses: actions/setup-node@v1 |
||||
# with: |
||||
# node-version: '12.x' |
||||
# |
||||
# - name: cache dependencies |
||||
# uses: actions/cache@v1 |
||||
# with: |
||||
# path: ~/.npm |
||||
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} |
||||
# restore-keys: | |
||||
# ${{ runner.os }}-node- |
||||
# - run: npm install |
||||
# - run: npm run lint:style |
||||
# |
||||
# lintdocs: |
||||
# name: documentation lint |
||||
# runs-on: ubuntu-latest |
||||
# needs: [lintcode,lintstyle] |
||||
# steps: |
||||
# - name: checkout |
||||
# uses: actions/checkout@v2 |
||||
# |
||||
# - name: setup node |
||||
# uses: actions/setup-node@v1 |
||||
# with: |
||||
# node-version: '12.x' |
||||
# |
||||
# - name: cache dependencies |
||||
# uses: actions/cache@v1 |
||||
# with: |
||||
# path: ~/.npm |
||||
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} |
||||
# restore-keys: | |
||||
# ${{ runner.os }}-node- |
||||
# |
||||
# - run: npm install |
||||
# - run: npm run lint:markdown |
||||
|
||||
tests: |
||||
name: Meteor ${{ matrix.meteor }} tests |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
|
||||
# CHECKOUTS |
||||
- name: Checkout |
||||
uses: actions/checkout@v2 |
||||
|
||||
# CACHING |
||||
- name: Install Meteor |
||||
id: cache-meteor-install |
||||
uses: actions/cache@v2 |
||||
with: |
||||
path: ~/.meteor |
||||
key: v1-meteor-${{ hashFiles('.meteor/versions') }} |
||||
restore-keys: | |
||||
v1-meteor- |
||||
|
||||
- name: Cache NPM dependencies |
||||
id: cache-meteor-npm |
||||
uses: actions/cache@v2 |
||||
with: |
||||
path: ~/.npm |
||||
key: v1-npm-${{ hashFiles('package-lock.json') }} |
||||
restore-keys: | |
||||
v1-npm- |
||||
|
||||
- name: Cache Meteor build |
||||
id: cache-meteor-build |
||||
uses: actions/cache@v2 |
||||
with: |
||||
path: | |
||||
.meteor/local/resolver-result-cache.json |
||||
.meteor/local/plugin-cache |
||||
.meteor/local/isopacks |
||||
.meteor/local/bundler-cache/scanner |
||||
key: v1-meteor_build_cache-${{ github.ref }}-${{ github.sha }} |
||||
restore-key: | |
||||
v1-meteor_build_cache- |
||||
|
||||
- name: Setup meteor |
||||
uses: meteorengineer/setup-meteor@v1 |
||||
with: |
||||
meteor-release: '2.2' |
||||
|
||||
- name: Install NPM Dependencies |
||||
run: meteor npm ci |
||||
|
||||
- name: Run Tests |
||||
run: sh ./test-wekan.sh -cv |
||||
|
||||
- name: Upload coverage |
||||
uses: actions/upload-artifact@v2 |
||||
with: |
||||
name: coverage-folder |
||||
path: .coverage/ |
||||
|
||||
coverage: |
||||
name: Coverage report |
||||
runs-on: ubuntu-latest |
||||
needs: [tests] |
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v2 |
||||
|
||||
- name: Download coverage |
||||
uses: actions/download-artifact@v2 |
||||
with: |
||||
name: coverage-folder |
||||
path: .coverage/ |
||||
|
||||
|
||||
- name: Coverage Report |
||||
uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 |
||||
with: |
||||
path: ".coverage/lcov.info" |
||||
min_coverage: 1 # TODO add tests and increase to 95! |
@ -0,0 +1,197 @@ |
||||
/* 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'); |
||||
}); |
||||
}); |
||||
|
||||
describe(Utils.myCardsSort.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.myCardsSortToggle.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.setMyCardsSort.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.archivedBoardIds.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.dueCardsView.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.setDueCardsView.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.goBoardId.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.goCardId.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.processUploadedAttachment.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.shrinkImage.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.capitalize.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.isMiniScreen.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.isShowDesktopDragHandles.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.isMiniScreenOrShowDesktopDragHandles.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.calculateIndexData.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.calculateIndex.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.isTouchDevice.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.calculateTouchDistance.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.enableClickOnTouch.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.manageCustomUI.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.setCustomUI.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.setMatomo.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.manageMatomo.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
|
||||
describe(Utils.getTriggerActionDesc.name, function() { |
||||
it('has no tests yet'); |
||||
}); |
||||
}); |
@ -0,0 +1 @@ |
||||
import './Utils.tests'; |
File diff suppressed because it is too large
Load Diff
@ -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); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,98 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
|
||||
set -e |
||||
|
||||
# ------------------------------------------ |
||||
# |
||||
# Variable declarations |
||||
# |
||||
# ------------------------------------------ |
||||
|
||||
PROJECT_ROOT=$(pwd) |
||||
PORT=4040 |
||||
RUN_ONCE='--once' |
||||
VERBOSE_MODE=0 |
||||
WATCH_MODE=0 |
||||
COVERAGE=0 |
||||
|
||||
# ------------------------------------------ |
||||
# |
||||
# Read args from script call |
||||
# |
||||
# ------------------------------------------ |
||||
|
||||
while getopts "vcw" opt; do |
||||
case $opt in |
||||
v) |
||||
VERBOSE_MODE=1 |
||||
;; |
||||
c) |
||||
COVERAGE=1 |
||||
;; |
||||
w) |
||||
WATCH_MODE=1 |
||||
RUN_ONCE='' |
||||
;; |
||||
\?) |
||||
echo "Invalid option: -$OPTARG" >&2 |
||||
exit 1 |
||||
;; |
||||
esac |
||||
done |
||||
|
||||
# ------------------------------------------ |
||||
# |
||||
# Print variables on verbose mode |
||||
# |
||||
# ------------------------------------------ |
||||
|
||||
if [ "$VERBOSE_MODE" -eq "1" ]; |
||||
then |
||||
PROJECT_NAME=`basename "$PROJECT_ROOT"` |
||||
echo "=> Test $PROJECT_NAME" |
||||
echo "=> Path: [${PROJECT_ROOT}]" |
||||
echo "=> Port: [${PORT}]" |
||||
echo "=> Watch mode: [${WATCH_MODE}] ${RUN_ONCE}" |
||||
echo "=> COVERAGE: [${COVERAGE}]" |
||||
fi |
||||
|
||||
|
||||
if [ "$WATCH_MODE" -eq "0" ]; |
||||
then |
||||
# --------------------------------------------------------------- |
||||
# in cli mode we use a headless browser to include client tests |
||||
# and we activate the coverage reporting functionality |
||||
# --------------------------------------------------------------- |
||||
BABEL_ENV=COVERAGE \ |
||||
TEST_BROWSER_DRIVER=puppeteer \ |
||||
TEST_SERVER=1 \ |
||||
TEST_CLIENT=1 \ |
||||
COVERAGE=${COVERAGE} \ |
||||
COVERAGE_OUT_HTML=1 \ |
||||
COVERAGE_OUT_LCOVONLY=1 \ |
||||
COVERAGE_OUT_TEXT_SUMMARY=1 \ |
||||
COVERAGE_OUT_JSON_SUMMARY=1 \ |
||||
COVERAGE_APP_FOLDER=$PWD/ \ |
||||
COVERAGE_VERBOSE_MODE=${VERBOSE_MODE} \ |
||||
meteor test \ |
||||
--exclude-archs=web.browser.legacy,web.cordova \ |
||||
--driver-package=meteortesting:mocha \ |
||||
--settings=settings.json \ |
||||
--port=${PORT} \ |
||||
--once |
||||
cat ./.coverage/summary.txt |
||||
else |
||||
# --------------------------------------------------------------- |
||||
# in watch mode we neither use a browser driver, nor coverage |
||||
# se we speed up the test reload in the development phase |
||||
# --------------------------------------------------------------- |
||||
TEST_BROWSER_DRIVER=puppeteer \ |
||||
TEST_SERVER=1 \ |
||||
TEST_CLIENT=1 \ |
||||
meteor test \ |
||||
--exclude-archs=web.browser.legacy,web.cordova \ |
||||
--driver-package=meteortesting:mocha \ |
||||
--settings=settings.json \ |
||||
--port=${PORT} |
||||
fi |
@ -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'; |
||||
}); |
||||
} |
Loading…
Reference in new issue