...
 
Commits (2)
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
......@@ -4,9 +4,6 @@
image: node:10
before_script:
# To prevent TypeError: log.gauge.isEnabled is not a function
# See: https://github.com/npm/npmlog/issues/48
- rm -rf node_modules/npm
- yarn || echo "yarn not found, this is expected if we are on the deploy steps"
cache:
......@@ -21,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
- au build
- au test --coverage
except:
- triggers
......@@ -47,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}) => {
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,9 @@
"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",
"lint": "nps lint",
"translate": "nps update.translations"
"start": "au run --watch",
"test": "au test"
},
"repository": {
"type": "git",
......@@ -17,46 +14,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 +35,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 +53,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 +73,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 +95,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';
karma.config.args.forEach(function(value, index) {
if (value === 'aurelia-root') {
root = karma.config.args[index + 1];
}
});
if (!karma || !requirejs) {
return;
}
function normalizePath(path) {
var normalized = []
var parts = path
.split('?')[0] // cut off GET params, used by noext requirejs plugin
.split('/')
for (var i = 0; i < parts.length; i++) {
if (parts[i] === '.') {
continue
}
if (parts[i] === '..' && normalized.length && normalized[normalized.length - 1] !== '..') {
normalized.pop()
continue
}
normalized.push(parts[i])
}
// Use case of testing source code. RequireJS doesn't add .js extension to files asked via sibling selector
// If normalized path doesn't include some type of extension, add the .js to it
if (normalized.length > 0 && normalized[normalized.length - 1].indexOf('.') < 0) {
normalized[normalized.length - 1] = normalized[normalized.length - 1] + '.js'
}
return normalized.join('/')
}
function patchRequireJS(files, originalLoadFn, locationPathname) {
var IS_DEBUG = /debug\.html$/.test(locationPathname)
requirejs.load = function (context, moduleName, url) {
url = normalizePath(url)
if (files.hasOwnProperty(url) && !IS_DEBUG) {
url = url + '?' + files[url]
}
if (url.indexOf('/base') !== 0) {
url = '/base/' + url;
}
return originalLoadFn.call(this, context, moduleName, url)
}
var originalDefine = global.define;
global.define = function(name, deps, m) {
if (typeof name === 'string') {
// alias from module "/base/root/name" to module "name"
originalDefine('/base/' + root + '/' + name, [name], function (result) { return result; });
}
// normal module define("name")
return originalDefine(name, deps, m);
};
global.define.amd = originalDefine.amd;
}
function requireTests() {
var TEST_REGEXP = /(spec)\.js$/i;
var allTestFiles = [];
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
allTestFiles.push(file);
}
});
require(['/base/test/unit/setup.js'], function() {
require(allTestFiles, window.__karma__.start);
});
}
karma.loaded = function() {}; // make it async
patchRequireJS(karma.files, requirejs.load, locationPathname);
requireTests();
})(window);
......@@ -18,23 +18,13 @@
*/
import 'aurelia-polyfills';
import { initialize } from 'aurelia-pal-browser';
import {Options} from 'aurelia-loader-nodejs';
import {globalize} from 'aurelia-pal-nodejs';
import path from 'path';
initialize();
Options.relativeToDir = path.join(__dirname, 'unit');
globalize();
global.navigator = { userAgent: 'Jest test runner' };
function values(obj) {
let vals = [];
for (let key in obj) {
if (obj.hasOwnProperty(key) && obj.propertyIsEnumerable(key)) {
vals.push(obj[key]);
}
}
return vals;
}
if (typeof Object.values !== 'function') {
Object.values = values;
}
global.window.IS_TESTING = true;
......@@ -64,10 +64,10 @@ describe('game/create', () => {
});
it('should activate', () => {
spyOn(mockedEas, 'subscribe');
spyOn(mockedEa, 'subscribe');
spyOn(sut, 'init');
spyOn(sut, '_joinGame');
jest.spyOn(mockedEas, 'subscribe');
jest.spyOn(mockedEa, 'subscribe');
jest.spyOn(sut, 'init');
jest.spyOn(sut, '_joinGame');
sut.activate({id: 'game_id'});
......@@ -80,8 +80,8 @@ describe('game/create', () => {
});
it('should deregister api callbacks on deactivation', () => {
spyOn(mockedEas, 'dispose');
spyOn(mockedBes, 'dispose');
jest.spyOn(mockedEas, 'dispose');
jest.spyOn(mockedBes, 'dispose');
sut.deactivate();
......@@ -90,12 +90,12 @@ describe('game/create', () => {
});
it('should reset with init method', () => {
spyOn(sut, '_registerEvents');
spyOn(mockedApi, 'init');
spyOn(Wait, 'flushCache');
spyOn(sut, 'initPlayerInfos');
spyOn(mockedHistory, 'init');
spyOn(mockedBes, 'subscribe');
jest.spyOn(sut, '_registerEvents');
jest.spyOn(mockedApi, 'init');
jest.spyOn(Wait, 'flushCache');
jest.spyOn(sut, 'initPlayerInfos');
jest.spyOn(mockedHistory, 'init');
jest.spyOn(mockedBes, 'subscribe');
sut.init({id: 'game_id'});
......@@ -105,13 +105,13 @@ describe('game/create', () => {
expect(mockedHistory.init).toHaveBeenCalled();
expect(sut.initPlayerInfos);
expect(mockedBes.subscribe)
.toHaveBeenCalledWith(sut.playerInfos, 'name', jasmine.any(Function));
.toHaveBeenCalledWith(sut.playerInfos, 'name', expect.any(Function));
expect(mockedBes.subscribe)
.toHaveBeenCalledWith(sut.playerInfos, 'hero', jasmine.any(Function));
.toHaveBeenCalledWith(sut.playerInfos, 'hero', expect.any(Function));
});
it('should initialize player infos', () => {
spyOn(mockedStorage, 'loadPlayerInfos').and.returnValue({});
jest.spyOn(mockedStorage, 'loadPlayerInfos').mockReturnValue({});
sut.initPlayerInfos();
......@@ -120,59 +120,59 @@ describe('game/create', () => {
expect(sut.playerInfos.hero.length).toBeGreaterThan(0);
});
it('should clear player id when reconnecting to the game and the slot was freed', () => {
it('should clear player id when reconnecting to the game and the slot was freed', async() => {
let joinGame = new Promise((resolve, reject) => reject(new Error()));
spyOn(mockedApi, 'joinGame').and.returnValue(joinGame);
jest.spyOn(mockedApi, 'joinGame').mockReturnValue(joinGame);
// Since we have a nested call to _joinGame, we need to use a state of the storage to
// avoid an infinite recursion.
let storageCleared = false;
spyOn(mockedStorage, 'retrievePlayerId').and.callFake(() => {
jest.spyOn(mockedStorage, 'retrievePlayerId').mockImplementation(() => {
if (!storageCleared) {
return 'player_id';
}
return null;
});
spyOn(mockedStorage, 'clearGameData').and.callFake(() => {
jest.spyOn(mockedStorage, 'clearGameData').mockImplementation(() => {
storageCleared = true;
});
spyOn(sut._logger, 'warn');
jest.spyOn(sut._logger, 'warn');
sut.gameId = 'game_id';
return sut._joinGame().then(() => {
expect(mockedApi.joinGame)
.toHaveBeenCalledWith({gameId: 'game_id', playerId: 'player_id'});
expect(sut._logger.warn).toHaveBeenCalledWith('Failed to join the game', new Error());
expect(mockedStorage.clearGameData).toHaveBeenCalledWith('game_id');
}, () => fail('Unwanted code branch'));
await sut._joinGame();
expect(mockedApi.joinGame)
.toHaveBeenCalledWith({gameId: 'game_id', playerId: 'player_id'});
expect(sut._logger.warn).toHaveBeenCalledWith('Failed to join the game', new Error());
expect(mockedStorage.clearGameData).toHaveBeenCalledWith('game_id');
});
it('should join the game from an id', () => {
spyOn(mockedStorage, 'retrievePlayerId').and.returnValue('player_id');
spyOn(mockedApi, 'joinGame').and.returnValue(new Promise(resolve => resolve()));
it('should join the game from an id', async() => {
jest.spyOn(mockedStorage, 'retrievePlayerId').mockReturnValue('player_id');
jest.spyOn(mockedApi, 'joinGame').mockReturnValue(new Promise(resolve => resolve()));
sut.playerInfos = {};
sut.gameId = 'game_id';
return sut._joinGame().then(() => {
expect(mockedStorage.retrievePlayerId).toHaveBeenCalledWith('game_id');
expect(mockedApi.joinGame)
.toHaveBeenCalledWith({gameId: 'game_id', playerId: 'player_id'});
}, () => fail('Unwanted code branch'));
await sut._joinGame();
expect(mockedStorage.retrievePlayerId).toHaveBeenCalledWith('game_id');
expect(mockedApi.joinGame)
.toHaveBeenCalledWith({gameId: 'game_id', playerId: 'player_id'});
});
it('should navigate to initialize the game if no id param', () => {
spyOn(mockedApi, 'initializeGame');
jest.spyOn(mockedApi, 'initializeGame');
sut.activate();
expect(mockedApi.initializeGame)
.toHaveBeenCalledWith(jasmine.any(String), jasmine.any(String));
.toHaveBeenCalledWith(expect.any(String), expect.any(String));
expect(sut.gameId).toBeUndefined();
});
it('should navigate to {version}/create/{id} after game initialization', () => {
let gameInitializedData = {game_id: 'the_game_id'};
spyOn(mockedRouter, 'navigateToRoute');
jest.spyOn(mockedRouter, 'navigateToRoute');
sut.activate();
mockedEas.publish('aot:api:game_initialized', gameInitializedData);
......@@ -198,7 +198,7 @@ describe('game/create', () => {
mockedEas
);
let gameInitializedData = {game_id: 'the_game_id'};
spyOn(mockedRouter, 'navigateToRoute');
jest.spyOn(mockedRouter, 'navigateToRoute');
sut.activate();
mockedEas.publish('aot:api:game_initialized', gameInitializedData);
......@@ -213,7 +213,7 @@ describe('game/create', () => {
});
it('should set the 2nd slot to AI after game initilization', () => {
spyOn(mockedApi, 'updateSlot');
jest.spyOn(mockedApi, 'updateSlot');
mockedState._me = {
name: 'Player 1',
......@@ -235,14 +235,14 @@ describe('game/create', () => {
sut._autoAddAi();
expect(mockedApi.updateSlot).toHaveBeenCalled();
let args = mockedApi.updateSlot.calls.mostRecent().args[0];
let args = mockedApi.updateSlot.mock.calls.slice(-1)[0][0];
expect(args.state).toBe('AI');
expect(args.player_name).toBe('AI undefined');
expect(args.hero).toBeDefined();
});
it('should not set the 2nd slot to AI if player changed a slot', () => {
spyOn(mockedApi, 'updateSlot');
jest.spyOn(mockedApi, 'updateSlot');
mockedState._me = {
name: 'Player 1',
......@@ -268,7 +268,7 @@ describe('game/create', () => {
});
it('should create game', () => {
spyOn(mockedApi, 'createGame');
jest.spyOn(mockedApi, 'createGame');
sut.createGame();
......@@ -276,7 +276,7 @@ describe('game/create', () => {
});
it('should navigate to play/{id} after the game creation', () => {
spyOn(mockedRouter, 'navigateToRoute');
jest.spyOn(mockedRouter, 'navigateToRoute');
sut.creating = true;
sut.activate({id: 'the_game_id'});
......
......@@ -31,7 +31,7 @@ describe('the Game Layout module', () => {
});
it('should init the history', () => {
spyOn(mockedHistory, 'init');
jest.spyOn(mockedHistory, 'init');
const sut = new Layout(mockedHistory); // eslint-disable-line
......
......@@ -42,7 +42,7 @@ describe('play', () => {
});
it('should register api callbacks on activation', () => {
spyOn(mockedEas, 'subscribe');
jest.spyOn(mockedEas, 'subscribe');
sut.activate();
......@@ -50,7 +50,7 @@ describe('play', () => {
});
it('should deregister api callbacks on deactivation', () => {
spyOn(mockedEas, 'dispose');
jest.spyOn(mockedEas, 'dispose');
sut.deactivate();
......@@ -58,7 +58,7 @@ describe('play', () => {
});
it('should ask to join game in no name', () => {
spyOn(mockedApi, 'joinGame');
jest.spyOn(mockedApi, 'joinGame');
mockedApi._me = {};
sut.activate({id: 'game_id'});
......@@ -67,7 +67,7 @@ describe('play', () => {
});
it('should not ask to join the game if a name is supplied', () => {
spyOn(mockedApi, 'joinGame');
jest.spyOn(mockedApi, 'joinGame');
mockedState._me = {name: 'Player 1'};
sut.activate({id: 'game_id'});
......@@ -75,22 +75,23 @@ describe('play', () => {
expect(mockedApi.joinGame).not.toHaveBeenCalled();
});
it('should display the game over popup on game over', () => {
spyOn(mockedPopup, 'display');
it('should display the game over popup on game over', async() => {
jest.spyOn(mockedPopup, 'display');
mockedApi._gameOverDeferred.resolve(['Player 1', 'Player 2']);
sut.activate();
return mockedApi.onGameOverDeferred.then(() => {
expect(mockedPopup.display).toHaveBeenCalledWith(
'game-over',
{message: ['Player 1', 'Player 2']});
}, () => fail('Unwanted code branch'));
await mockedApi.onGameOverDeferred;
expect(mockedPopup.display).toHaveBeenCalledWith(
'game-over',
{message: ['Player 1', 'Player 2']},
);
});
describe('special actions', () => {
it('should log error for unknown action', () => {
spyOn(sut._logger, 'error');
jest.spyOn(sut._logger, 'error');
let action = {
special_action_name: 'toto',
};
......@@ -109,14 +110,14 @@ describe('play', () => {
let action = {
special_action_name: 'assassination',
};
spyOn(sut._api, 'viewPossibleActions');
jest.spyOn(sut._api, 'viewPossibleActions');
mockedState.game.your_turn = true;
mockedState.me.index = 0;
sut._handleSpecialActionNotify(action);
expect(sut.pawnClickable).toBe(true);
expect(sut.onPawnClicked).toEqual(jasmine.any(Function));
expect(sut.onPawnClicked).toEqual(expect.any(Function));
sut.onPawnClicked(0);
expect(sut._api.viewPossibleActions).toHaveBeenCalledWith({
name: 'assassination',
......@@ -134,12 +135,12 @@ describe('play', () => {
special_action_name: 'Assassination',
});
expect(sut.onPawnSquareClicked).toEqual(jasmine.any(Function));
expect(sut.onPawnSquareClicked).toEqual(expect.any(Function));
expect(sut.pawnClickable).toBe(true);
expect(sut.onPawnClicked).toEqual(jasmine.any(Function));
expect(sut.onPawnClicked).toEqual(expect.any(Function));
expect(sut.pawnsForcedNotClickable).toEqual([0]);
spyOn(mockedApi, 'playSpecialAction');
jest.spyOn(mockedApi, 'playSpecialAction');
sut.onPawnSquareClicked('square-0-0', 0, 0, 0);
......
......@@ -36,22 +36,22 @@ describe('board', () => {
});
it('should register callbacks', () => {
spyOn(mockedEas, 'subscribe');
jest.spyOn(mockedEas, 'subscribe');
sut = new AotBoardCustomElement(mockedApi, mockedEas);
expect(mockedEas.subscribe).toHaveBeenCalled();
expect(mockedEas.subscribe.calls.argsFor(0)[0]).toBe('aot:api:view_possible_squares');
expect(mockedEas.subscribe.calls.argsFor(1)[0]).toBe('aot:api:player_played');
expect(mockedEas.subscribe.calls.argsFor(2)[0]).toBe('aot:api:play');
expect(mockedEas.subscribe.calls.argsFor(3)[0])
expect(mockedEas.subscribe.mock.calls[0][0]).toBe('aot:api:view_possible_squares');
expect(mockedEas.subscribe.mock.calls[1][0]).toBe('aot:api:player_played');
expect(mockedEas.subscribe.mock.calls[2][0]).toBe('aot:api:play');
expect(mockedEas.subscribe.mock.calls[3][0])
.toBe('aot:api:play_trump');
expect(mockedEas.subscribe.calls.argsFor(4)[0])
expect(mockedEas.subscribe.mock.calls[4][0])
.toBe('aot:api:special_action_view_possible_actions');
});
it('should dispose of subscriptions', () => {
spyOn(mockedEas, 'dispose');
jest.spyOn(mockedEas, 'dispose');
sut.unbind();
......@@ -79,7 +79,7 @@ describe('board', () => {
});
it('should move to on possible square', () => {
spyOn(mockedApi, 'play');
jest.spyOn(mockedApi, 'play');
sut.possibleSquares = ['square-0-0'];
sut.selectedCard = {name: 'King', color: 'red'};
......@@ -96,7 +96,7 @@ describe('board', () => {
});
it('should only move on possible square', () => {
spyOn(mockedApi, 'play');
jest.spyOn(mockedApi, 'play');
sut.possibleSquares = ['square-0-0'];
sut.selectedCard = {name: 'King', color: 'red'};
......@@ -106,7 +106,7 @@ describe('board', () => {
});
it('should not move if no possible squares', () => {
spyOn(mockedApi, 'play');
jest.spyOn(mockedApi, 'play');
sut.selectedCard = {name: 'King', color: 'red'};
sut.handleSquareClicked('square-1-0', 0, 0, {isArrivalSquare: false});
......@@ -115,7 +115,7 @@ describe('board', () => {
});
it('should not move if no selected card', () => {
spyOn(mockedApi, 'play');
jest.spyOn(mockedApi, 'play');
sut.possibleSquares = ['square-0-0'];
sut.selectedCard = null;
......@@ -133,7 +133,7 @@ describe('board', () => {
describe('pawn clicked', () => {
it('should not do anything if pawnClickabel is false', () => {
spyOn(sut, 'onPawnClicked');
sut.onPawnClicked = jest.fn();
sut.pawnClicked();
......@@ -141,7 +141,7 @@ describe('board', () => {
});
it('should not do anything if index is excluded from clickable list', () => {
spyOn(sut, 'onPawnClicked');
sut.onPawnClicked = jest.fn();
sut.pawnClickable = true;
sut.pawnsForcedNotClickable = [0];
......@@ -151,7 +151,7 @@ describe('board', () => {
});
it('should call cb if pawnClickabel is true', () => {
spyOn(sut, 'onPawnClicked');
sut.onPawnClicked = jest.fn();
sut.pawnClickable = true;
sut.pawnClicked(0);
......@@ -160,12 +160,12 @@ describe('board', () => {
});
it('should move to if possible squares and pawn', () => {
spyOn(mockedApi, 'playSpecialAction');
jest.spyOn(mockedApi, 'playSpecialAction');
sut.possibleSquares = ['square-0-0'];
sut._selectedPawnIndex = 0;
sut._actionName = 'action';
sut.onPawnSquareClicked = () => {};
spyOn(sut, 'onPawnSquareClicked');
sut.onPawnSquareClicked = jest.fn();
sut.handleSquareClicked('square-0-0', 0, 0, {isArrivalSquare: false});
......
......@@ -57,7 +57,7 @@ describe('cards', () => {
it('should view possible movements', () => {
let card = {name: 'King', color: 'red'};
spyOn(mockedApi, 'viewPossibleMovements');
jest.spyOn(mockedApi, 'viewPossibleMovements');
mockedState._game.your_turn = true;
mockedState._game.has_remaining_moves_to_play = true;
......@@ -69,7 +69,7 @@ describe('cards', () => {
it('should not view possible movement if not your turn', () => {
let card = {name: 'King', color: 'red'};
spyOn(mockedApi, 'viewPossibleMovements');
jest.spyOn(mockedApi, 'viewPossibleMovements');
mockedState._game.your_turn = false;
sut.selectedCard = null;
......@@ -81,7 +81,7 @@ describe('cards', () => {
it('should not view possible movement if no move left', () => {
let card = {name: 'King', color: 'red'};
spyOn(mockedApi, 'viewPossibleMovements');
jest.spyOn(mockedApi, 'viewPossibleMovements');
mockedState._game.your_turn = true;
mockedState._game.has_remaining_moves_to_play = false;
sut.selectedCard = null;
......@@ -92,9 +92,9 @@ describe('cards', () => {
expect(mockedApi.viewPossibleMovements).not.toHaveBeenCalledWith();
});
it('should pass', () => {
spyOn(mockedPopup, 'display').and.callThrough();
spyOn(mockedApi, 'pass');
it('should pass', async() => {
jest.spyOn(mockedPopup, 'display');
jest.spyOn(mockedApi, 'pass');
sut.selectedCard = {name: 'King', color: 'red'};
sut.pass();
......@@ -107,15 +107,15 @@ describe('cards', () => {
},
}
);
return mockedPopup.popupPromise.then(() => {
expect(sut.selectedCard).toBe(null);
expect(mockedApi.pass).toHaveBeenCalled();
}, () => fail('Unwanted code branch'));
await mockedPopup.popupPromise;
expect(sut.selectedCard).toBe(null);
expect(mockedApi.pass).toHaveBeenCalled();
});
it('should pass action', () => {
spyOn(mockedPopup, 'display').and.callThrough();
spyOn(mockedApi, 'passSpecialAction');
it('should pass action', async() => {
jest.spyOn(mockedPopup, 'display');
jest.spyOn(mockedApi, 'passSpecialAction');
sut.specialActionInProgress = true;
sut.specialActionName = 'assassination';
......@@ -129,15 +129,14 @@ describe('cards', () => {
},
}
);
return mockedPopup.popupPromise.then(() => {
expect(mockedApi.passSpecialAction).toHaveBeenCalledWith('assassination');
}, () => fail('Unwanted code branch'));
await mockedPopup.popupPromise;
expect(mockedApi.passSpecialAction).toHaveBeenCalledWith('assassination');
});
it('should not pass on cancel', () => {
it('should not pass on cancel', async() => {
let promise = Promise.reject(new Error());
spyOn(mockedPopup, 'display').and.returnValue(promise);
spyOn(mockedApi, 'pass');
jest.spyOn(mockedPopup, 'display').mockReturnValue(promise);
jest.spyOn(mockedApi, 'pass');
sut.pass();
......@@ -150,14 +149,13 @@ describe('cards', () => {
}
);
return promise.then(() => fail('Unwanted code branch'), () => {
expect(mockedApi.pass).not.toHaveBeenCalled();
}, () => fail('Unwanted code branch'));
await expect(promise).rejects.toThrow();
expect(mockedApi.pass).not.toHaveBeenCalled();
});
it('should discard a card', () => {
spyOn(mockedApi, 'discard');
spyOn(mockedPopup, 'display').and.callThrough();
it('should discard a card', async() => {
jest.spyOn(mockedApi, 'discard');
jest.spyOn(mockedPopup, 'display');
sut.selectedCard = {
name: 'King',
color: 'red',
......@@ -175,18 +173,17 @@ describe('cards', () => {
}
);
return mockedPopup.popupPromise.then(() => {
expect(mockedApi.discard).toHaveBeenCalledWith({
cardName: 'King',
cardColor: 'red',
});
expect(sut.selectedCard).toBe(null);
}, () => fail('Unwanted code branch'));
await mockedPopup.popupPromise;
expect(mockedApi.discard).toHaveBeenCalledWith({
cardName: 'King',
cardColor: 'red',
});
expect(sut.selectedCard).toBe(null);
});
it('should display a popup if no card is selected', () => {
spyOn(mockedApi, 'discard');
spyOn(mockedPopup, 'display');
jest.spyOn(mockedApi, 'discard');
jest.spyOn(mockedPopup, 'display');
sut.discard();
......@@ -204,7 +201,7 @@ describe('cards', () => {
describe('special action', () => {
it('should register callbacks', () => {
spyOn(mockedEas, 'subscribe');
jest.spyOn(mockedEas, 'subscribe');
sut =
new AotCardsCustomElement(mockedApi, mockedPopup, mockedI18n, mockedOl, mockedEas);
......@@ -214,9 +211,9 @@ describe('cards', () => {
it('should dispose subscriptions', () => {
let observerLocatorStubResults = new ObserverLocatorStubResults();
spyOn(mockedOl, 'getObserver').and.returnValue(observerLocatorStubResults);
spyOn(observerLocatorStubResults, 'unsubscribe');
spyOn(mockedEas, 'dispose');
jest.spyOn(mockedOl, 'getObserver').mockReturnValue(observerLocatorStubResults);
jest.spyOn(observerLocatorStubResults, 'unsubscribe');