Commit 02f0d71e authored by Julien Enselme's avatar Julien Enselme

chore(test): switch from karma to jest

parent df4c9f80
Pipeline #75262947 canceled with stages
in 3 minutes and 56 seconds
module.exports = api => {
api.cache.using(() => {
return 'babel:' + process.env.BABEL_TARGET;
});
return {
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
],
"presets": [
["@babel/preset-env", {
loose: true,
modules: process.env.BABEL_TARGET === 'node' ? 'commonjs' : false,
targets: process.env.BABEL_TARGET === 'node' ? {
node: "10",
} : undefined,
}],
],
}
};
......@@ -14,11 +14,11 @@ module.exports = {
// Node.js global variables and Node.js-specific rules
"node": true,
"es6": true,
"jasmine": true,
"jest": true,
},
"plugins": [
"babel",
"jasmine"
"jest"
],
"rules": {
"linebreak-style": [
......
/index.html
/node_modules
/dist
/coverage
/public
/scripts/rollbar.js
/app/environment.js
......@@ -10,3 +9,4 @@
npm-debug.log
yarn-error.log
/.env
/test/coverage-jest
......@@ -18,18 +18,15 @@ stages:
lint:
stage: checks
script:
- npm run --silent lint
- yarn run --silent lint
except:
- triggers
tests:
stage: checks
script:
- apt-get -qq update
- apt-get -qq install -y firefox-esr
- firefox --version
# We don't use chromium too because it is a bit harder to setup and won't bring much value here.
- npm run test -- -b FirefoxHeadless
- yarn run build
- yarn run test --coverage
except:
- triggers
......@@ -44,7 +41,7 @@ build:
- dist
expire_in: 1 week
script:
- npm run build -- --env $ENV --version $VERSION
- au build --env $ENV --version $VERSION
except:
- tags
......
......@@ -72,7 +72,9 @@ export class Create {
// We preload the board: it is big and can take a while to load on bad connections. So if
// a player reaches the create game page, we consider he/she will play. So it makes sense
// to start loading the board.
require(['game/play/widgets/board/board'], () => {});
if (!window.IS_TESTING) {
require(['game/play/widgets/board/board'], () => {});
}
}
activate(params = {}) {
......
......@@ -21,12 +21,12 @@
<require from="./counter.css"></require>
<div id="counter-countainer">
<canvas id="counter"
<canvas ref="counterCanvas"
if.bind="!specialActionInProgress"
class="centered-block ${displayCounter ? '' : 'hidden'}"
width="300"
height="300"></canvas>
<canvas id="counter-special-action"
<canvas ref="specialActionCounterCanvas"
if.bind="specialActionInProgress"
class="centered-block ${displayCounter ? '' : 'hidden'}"
width="300"
......
......@@ -40,6 +40,9 @@ const COUNTER_HEIGHT = 300;
@inject(Api, EventAggregatorSubscriptions, State)
export class AotCounterCustomElement {
_api;
// Filled by ref.
counterCanvas = null;
specialActionCounterCanvas = null;
constructor(api, eas, state) {
this._api = api;
......@@ -52,14 +55,10 @@ export class AotCounterCustomElement {
this._logger = LogManager.getLogger('AotCounterCustomElement');
this.startTime = null;
this.timerInterval = null;
this.canvas = null;
this.specialActionCanvas = null;
this.timeLeft = TIME_FOR_TURN;
this.angle = 0;
this.waitForCounter = Wait.forId('counter');
this.waitForSpecialActionCounter = Wait.forId('counter-special-action');
this.init();
this._eas.subscribe('aot:api:play', () => {
clearInterval(this.timerIntervalForSpecialAction);
this._handlePlayRequest();
......@@ -78,15 +77,19 @@ export class AotCounterCustomElement {
this._api.onReconnectDeferred.then(message => {
if (message.special_action_name) {
this._handleSpecialActionNotify(message);
this.initSpecialActionCounter(message.special_action_elapsed_time);
this.startSpecialActionCounter(message.special_action_elapsed_time);
}
});
this._eas.subscribe('aot:notifications:special_action_in_game_help_seen', () => {
this.initSpecialActionCounter();
this.startSpecialActionCounter();
});
}
attach() {
this.init();
}
_canStart() {
return this._state.game.your_turn && !this._state.game.game_over && this.startTime === null;
}
......@@ -116,32 +119,28 @@ export class AotCounterCustomElement {
}
init() {
if (this._state.game.your_turn && !this._state.game.game_over && this.startTime === null) {
if (this.counterCanvas === null) {
this._logger.debug('Counter canvas is not in the DOM yet. Init was called too soon.');
} else if (
this._state.game.your_turn
&& !this._state.game.game_over
&& this.startTime === null
) {
this._paused = false;
this.specialActionInProgress = false;
this.waitForCounter.then(canvas => {
this.canvas = canvas;
let elapsedTime = this._state.me.elapsed_time || 0;
this.maxTime = TIME_FOR_TURN - elapsedTime;
// Round max time to upper second
this.maxTime = Math.floor(this.maxTime / 1000) * 1000;
this._pausedDuration = 0;
// Draw the counter.
this.countDownClock();
});
let elapsedTime = this._state.me.elapsed_time || 0;
this.maxTime = TIME_FOR_TURN - elapsedTime;
// Round max time to upper second
this.maxTime = Math.floor(this.maxTime / 1000) * 1000;
this._pausedDuration = 0;
// Draw the counter.
this.countDownClock();
} else if (!this._state.game.your_turn) {
clearInterval(this.timerInterval);
this.startTime = null;
}
}
initSpecialActionCounter(elapsedTime = 0) {
this.waitForSpecialActionCounter.then(canvas => {
this.specialActionCanvas = canvas;
this.startSpecialActionCounter(elapsedTime);
});
}
pause() {
this._paused = true;
}
......@@ -187,8 +186,8 @@ export class AotCounterCustomElement {
this.angle -= 0.0001;
}
if (this.canvas && this.canvas.getContext) {
let ctx = this.canvas.getContext('2d');
if (this.counterCanvas && this.counterCanvas.getContext) {
let ctx = this.counterCanvas.getContext('2d');
// Clear canvas before re-drawing
ctx.clearRect(0, 0, COUNTER_WIDTH, COUNTER_HEIGHT);
......@@ -205,8 +204,10 @@ export class AotCounterCustomElement {
// Clock face ring
ctx.beginPath();
ctx.globalAlpha = 1;
/* eslint-disable */
ctx.arc(COUNTER_X, COUNTER_Y, COUNTER_RADIUS + 0.1, - 1.57, - 1.57 + this.angle, false);
ctx.arc(COUNTER_X, COUNTER_Y, 105, - 1.57 + this.angle, Math.PI * 2 - 1.57, true);
/* eslint-enable */
ctx.fillStyle = this.colourChanger();
ctx.fill();
ctx.closePath();
......@@ -266,8 +267,8 @@ export class AotCounterCustomElement {
countDownClockForSpecialAction() {
this.timeLeftForSpecialAction -= COUNTER_REFRESH_TIME;
if (this.specialActionCanvas && this.specialActionCanvas.getContext) {
let ctx = this.specialActionCanvas.getContext('2d');
if (this.specialActionCounterCanvas && this.specialActionCounterCanvas.getContext) {
let ctx = this.specialActionCounterCanvas.getContext('2d');
// Clear canvas before re-drawing
ctx.clearRect(0, 0, COUNTER_WIDTH, COUNTER_HEIGHT);
......
......@@ -97,9 +97,7 @@ export class Wait {
(function wait() {
let elementsWithClasses = element.getElementsByClassName(className);
// If jasmine is defined, we are running this in a unit test and must resolve the
// promise.
if (elementsWithClasses.length > 0 || window.jasmine) {
if (window.IS_TESTING || elementsWithClasses.length > 0) {
deferred.resolve(elementsWithClasses);
} else {
setTimeout(wait, 50);
......
......@@ -169,6 +169,10 @@ export class AssetSource {
}
static _preloadImages(images) {
if (window.IS_TESTING) {
return;
}
for (let src of images) {
let img = new Image();
img.src = `//${location.host}${src}`;
......
......@@ -238,15 +238,6 @@ export class StorageStub {
}
export class LocalStorageStub {
setItem() {
}
getItem() {
}
}
export class WsStub {
send(data) {
}
......@@ -312,7 +303,7 @@ export class EventAggregatorStub {
export class BindingEngineStub {
propertyObserver(object, property) {
this.propertyObserverObj = jasmine.createSpyObj('propertyObserver', ['subscribe']);
this.propertyObserverObj = {subscribe: jest.fn()};
return this.propertyObserverObj;
}
}
......
......@@ -314,20 +314,19 @@
},
{
"name": "i18next",
"path": "../node_modules/i18next/dist/commonjs",
"main": "index"
"path": "../node_modules/i18next/dist/cjs",
"main": "i18next"
},
{
"name": "i18next-xhr-backend",
"path": "../node_modules/i18next-xhr-backend/dist/commonjs",
"main": "index"
"path": "../node_modules/i18next-xhr-backend/dist/cjs",
"main": "i18nextXHRBackend"
}
]
},
{
"name": "bootstrap-bundle.js",
"prepend": [
"node_modules/bluebird/js/browser/bluebird.js",
"scripts/polyfills.js",
{
"name": "rollbar",
......
import gulp from 'gulp';
import {Server as Karma} from 'karma';
import jest from 'jest-cli';
import path from 'path';
import packageJson from '../../package.json';
import {CLIOptions} from 'aurelia-cli';
import build from './build';
import watch from './watch';
import * as path from 'path';
let karma = done => {
new Karma({
configFile: path.join(__dirname, '/../../karma.conf.js'),
singleRun: !CLIOptions.hasFlag('watch')
}, done).start();
};
export default (cb) => {
let options = packageJson.jest;
let unit;
Object.assign(options, {
collectCoverage: CLIOptions.hasFlag('coverage'),
watch: CLIOptions.hasFlag('watch'),
});
if (CLIOptions.hasFlag('watch')) {
unit = gulp.series(
build,
gulp.parallel(
done => { watch(); done(); },
karma
)
);
} else {
unit = gulp.series(
build,
karma
);
}
if (CLIOptions.getFlagValue('test-path-pattern')) {
Object.assign(options, {testPathPattern: [CLIOptions.getFlagValue('test-path-pattern')]});
}
export { unit as default };
process.env.BABEL_TARGET = 'node';
jest.runCLI(options, [path.resolve(__dirname, '../../')]).then(({results}) => {
console.log('nasuinrstaeinrts', results.numFailedTests, results.numFailedTestSuites);
if (results.numFailedTests || results.numFailedTestSuites) {
cb('Tests Failed');
} else {
cb();
}
});
};
{
"name": "test",
"description": "Runs all unit tests and reports the results.",
"flags": [
{
"name": "env",
"description": "Sets the build environment.",
"type": "string"
},
{
"name": "watch",
"description": "Watches test files for changes and re-runs the tests automatically.",
"type": "boolean"
},
{
"name": "coverage",
"description": "Run tests with code coverage",
"type": "boolean"
}
]
"name": "test",
"description": "Runs Jest and reports the results.",
"flags": [
{
"name": "watch",
"description": "Watches test files for changes and re-runs the tests automatically.",
"type": "boolean"
},
{
"name": "test-path-pattern",
"description": "Run only tests matching this pattern",
"type": "string"
},
{
"name": "coverage",
"description": "Collect coverage",
"type": "boolean"
}
]
}
/*
* Copyright (C) 2015-2016 by Arena of Titans Contributors.
*
* This file is part of Arena of Titans.
*
* Arena of Titans is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Arena of Titans is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Arena of Titans. If not, see <http://www.gnu.org/licenses/>.
*/
"use strict";
const path = require('path');
const project = require('./aurelia_project/aurelia.json');
let argv = require('minimist')(process.argv.slice(2));
let testSrc = [
{pattern: project.unitTestRunner.source, included: false},
'test/aurelia-karma.js',
];
let output = project.build.targets[0].output;
let appSrc = project.build.bundles.map(x => path.join(output, x.name));
let entryIndex = appSrc.indexOf(path.join(output, project.build.loader.configTarget));
let entryBundle = appSrc.splice(entryIndex, 1)[0];
let files = [entryBundle].concat(testSrc).concat(appSrc);
let browsers = [];
if (Array.isArray(argv.b)) {
browsers = browsers.concat(argv.b);
} else if (argv.b) {
browsers.push(argv.b);
}
if (browsers.length === 0) {
browsers = ['ChromeHeadless', 'FirefoxHeadless'];
}
let reporters = ['jasmine-diff', 'progress'];
let transpiler;
if (argv.coverage) {
transpiler = project.coverageTranspiler;
reporters.push('coverage');
} else {
transpiler = project.transpiler;
}
module.exports = function (config) {
config.set({
basePath: '.',
frameworks: [project.testFramework.id],
files: files,
exclude: [],
preprocessors: {
[project.unitTestRunner.source]: [transpiler.id],
},
babelPreprocessor: {options: transpiler.options},
coverageReporter: {
dir: project.paths.reports,
includeAllSources: true,
reporters: [
{type: 'cobertura', subdir: '.'},
{type: 'html', subdir: '.'},
{type: 'text'}
]
},
reporters: reporters,
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: browsers,
customLaunchers: {
'FirefoxHeadless': {
base: 'Firefox',
flags: [
'--headless'
],
}
},
singleRun: false,
// client.args must be a array of string.
// Leave 'aurelia-root', project.paths.root in this order so we can find
// the root of the aurelia project.
client: {
args: ['aurelia-root', project.paths.root]
},
browserConsoleLogOptions: {
level: 'log',
format: '%b %T: %m',
terminal: true
}
});
};
......@@ -25,5 +25,11 @@ module.exports = {
'stylelint "app/**/*.scss"',
'eslint "app/**/*.js" "test/**/*.js"'
),
test: {
default: series(
rimraf('test/coverage-jest'),
'jest',
),
},
},
};
......@@ -3,12 +3,10 @@
"version": "0.5.0",
"description": "Fight to death in the arena",
"scripts": {
"dev": "nps build.dev",
"build": "nps build.prod",
"test": "au test --coverage",
"tdd": "au test --watch",
"build": "au build",
"lint": "nps lint",
"translate": "nps update.translations"
"start": "au run --watch",
"test": "au test"
},
"repository": {
"type": "git",
......@@ -17,46 +15,19 @@
"dependencies": {
"au-rollbar": "^0.1.2",
"aurelia-animator-css": "^1.0.4",
"aurelia-binding": "^1.6.0",
"aurelia-bootstrapper": "^2.2.0",
"aurelia-dependency-injection": "^1.3.2",
"aurelia-event-aggregator": "^1.0.1",
"aurelia-framework": "^1.1.5",
"aurelia-history": "^1.1.0",
"aurelia-history-browser": "^1.1.0",
"aurelia-i18n": "^2.1.1",
"aurelia-loader": "^1.0.0",
"aurelia-loader-default": "^1.0.3",
"aurelia-logging": "^1.4.0",
"aurelia-logging-console": "^1.0.0",
"aurelia-metadata": "^1.0.3",
"aurelia-pal": "^1.5.0",
"aurelia-pal-browser": "^1.4.0",
"aurelia-path": "^1.1.1",
"aurelia-bootstrapper": "^2.3.2",
"aurelia-framework": "^1.3.1",
"aurelia-i18n": "^3.0.0-beta.7",
"aurelia-piwik": "^0.1.3",
"aurelia-polyfills": "^1.3.0",
"aurelia-route-recognizer": "^1.1.1",
"aurelia-router": "^1.5.0",
"aurelia-task-queue": "^1.2.1",
"aurelia-templating": "^1.7.0",
"aurelia-templating-binding": "^1.4.0",
"aurelia-templating-resources": "^1.5.2",
"aurelia-templating-router": "^1.3.1",
"bluebird": "^3.5.1",
"browserslist": "^4.3.5",
"css-animation-sync": "^0.0.6",
"aurelia-router": "^1.7.1",
"css-animation-sync": "^0.0.7",
"flexboxgrid": "^6.3.1",
"i18next": "^8.4.3",
"i18next-xhr-backend": "^1.4.2",
"i18next": "^17.0.9",
"i18next-xhr-backend": "^3.1.1",
"intl": "^1.2.5",
"reconnectingwebsocket": "^1.0.0",
"requirejs": "^2.3.5",
"source-list-map": "^2.0.0",
"source-map": "^0.5.6",
"source-map-resolve": "^0.5.0",
"source-map-support": "^0.4.16",
"source-map-url": "^0.4.0",
"text": "github:requirejs/text#latest"
"requirejs": "^2.3.6",
"text": "requirejs/text#latest"
},
"devDependencies": {
"@babel/core": "^7.4.0",
......@@ -65,13 +36,17 @@
"@babel/preset-env": "^7.4.2",
"@babel/register": "^7.4.0",
"aurelia-cli": "^1.0.2",
"aurelia-loader-nodejs": "^1.0.1",
"aurelia-pal-nodejs": "^1.2.0",
"aurelia-template-lint": "^0.10.0",
"aurelia-testing": "^1.0.0",
"aurelia-tools": "^2.0.0",
"babel-eslint": "^10.0.2",
"babel-jest": "^24.8.0",
"babel-plugin-istanbul": "^5.2.0",
"bluebird": "^3.5.5",
"browser-sync": "^2.26.7",
"browserslist": "^4.6.6",
"caniuse-db": "1.0.30000798",
"connect-history-api-fallback": "^1.6.0",
"csv-parse": "^2.0.0",
......@@ -79,7 +54,7 @@
"dotenv": "^4.0.0",
"eslint": "^6.1.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-jasmine": "^2.10.1",
"eslint-plugin-jest": "^22.14.1",
"esprima": "^4.0.1",
"glob": "^7.1.4",
"gulp": "^4.0.2",
......@@ -99,16 +74,11 @@
"gulp-sourcemaps": "^2.6.5",
"gulp-transform": "^3.0.5",
"gulp-watch": "^5.0.1",
"jasmine-core": "^3.4.0",
"jasmine-promises": "^0.4.1",
"jest": "^24.8.0",
"jest-cli": "^24.8.0",
"jest-localstorage-mock": "^2.4.0",
"jest-transform-stub": "^2.0.0",
"json-stable-stringify": "^1.0.1",
"karma": "^4.2.0",
"karma-babel-preprocessor": "^8.0.1",
"karma-chrome-launcher": "^3.0.0",
"karma-coverage": "^1.1.2",
"karma-firefox-launcher": "^1.1.0",
"karma-jasmine": "^2.0.1",
"karma-jasmine-diff-reporter": "^2.0.0",
"loggy": "^1.0.2",
"minimatch": "^3.0.4",
"minimist": "^1.2.0",
......@@ -126,6 +96,45 @@
"url": "^0.11.0",
"vinyl-fs": "^3.0.3"
},
"engines": {
"node": ">=10.14.0"
},
"jest": {
"moduleNameMapper": {
"^aurelia-binding$": "<rootDir>/node_modules/aurelia-binding"
},
"modulePaths": [
"<rootDir>/app",
"<rootDir>/node_modules"
],
"moduleFileExtensions": [
"js",
"json"
],
"transform": {
"^.+\\.(css|less|sass|scss|styl|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "jest-transform-stub",
"^.+\\.js$": "babel-jest"
},
"testRegex": "\\.spec\\.js$",
"setupFiles": [
"<rootDir>/test/jest-pretest.js",
"jest-localstorage-mock"
],
"testEnvironment": "node",
"collectCoverageFrom": [
"app/**/*.js",
"!**/*.spec.js",
"!**/node_modules/**",
"!**/test/**"
],
"coverageDirectory": "<rootDir>/test/coverage-jest",
"coverageReporters": [
"json",
"lcov",
"text",
"html"
]
},
"keywords": [
"game",
"board",
......
(function(global) {
var karma = global.__karma__;
var requirejs = global.requirejs
var locationPathname = global.location.pathname;
var root = 'src';