Skip to content

Commit

Permalink
Initiate game run (#36)
Browse files Browse the repository at this point in the history
* #30 init turn nb

* improve readme

* readme update complete

* Manage laod tests

* timeout correction

* failed correction partI

* partII

* partIII

* #30 trunLeft creation

* #30 cass error

* #30 add didacticiel tests

* #30 turnLeft decrement

* #30 correct run test

* #35 setup launch run

* #30 initiate action points

Co-authored-by: GUEDON Stephane <stephane.s.guedon@renault.com>
  • Loading branch information
fierfeu and GUEDON Stephane committed Oct 12, 2020
1 parent f17c317 commit 7d3f819
Show file tree
Hide file tree
Showing 22 changed files with 537 additions and 84 deletions.
61 changes: 58 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,65 @@
[travis-image]: https://travis-ci.org/fierfeu/Gayole.svg?branch=master
[travis-url]: https://travis-ci.org/fierfeu/Gayole

# Gayole
mon bac à sable ...
# Gayole Sand Box
**_This repository is a sand box for my own learning_**

You can acces to web site through Heroku at
## Strucutral choices
I began by working on a very light weight server in order to serve static page.
As I continue to developp a one site page to use this lw server I kept both back and front in the same project.
_it would be better to use dependencies between both_ and when doing so, i discover lws repo. **I need to benchmark some other lw server before to move to any one.**
That's the reaons why I keep, for the moment both back and front in the same project.

## Running
* You can Run app with
```
//supposing you're using PowerShell (like in VSCODE Terminal)
$ENV:PORT=80 // if you want to use this PORT on Windows, on Linux PORT=8080.
npm start
```
Ctrl+C to stop ....

## Testing
### Running Tests
You can run tests using npm run :
* You can run All unit tests with : _npm run unit-tests_ or directly with _mocha_
* You can run End to End tests with : _npm run e2e-test_
* All tests including E2E : _npm run All-tests_
* Running a given set of test by using strings iside brackets : _mocha -f [QOG_ for exemple

### tests organisation
test directory is organise in three main parts :
1. E2E :
Based on web driver you'll find all the hihgt level GUI tests for the game.
I began to use protractor but I had trouble to manage drag&drop whit this webdriver implementation and as i didn't used any framework Webdriver is simpler to use.
As I use private properties of ES6 I can only perform test with Chrome. Safari needs a MAc to test ... so SAFARi is not yet tested ... I have to investigate how to test Safari with Travis-ci.
2. Load tests
store all the Jmeter tests
3. UnitTests
All the units test for : tools used are mocha, chai and sinon JS
* App _contains all Back tests_)
* css _contains all tests performed on css files_ using JSDOM to verify class definitions
* HTML _contains allt tests on HTML index and board files_ tests also using JSDOM
* js _test js header for index html_
* json _tests scenarii json_
* mjs _contains all tests for ES6 modules_

**Load Tests** are described in the heroku part.

### why i used vanilla js and node

I don't like frameworks so i don't use frameworks for the moment.

##CI performed by travis-ci
please have intention in the chrome version ...

### site on heroku##
I use heroku as a first attemp to DevOps and plan to migrate on azure.
You can acces to [web site](https://gayole.herokuapp.com) through Heroku
[load-tests]:###load tests
Load tests are performed using jmeter+webdriver plugin and adress the [pre-production heroku app](https://gayole-web.herokuapp.com/).
load tests are stored in "_test\Load tests_"
I plan to test also Gatling.

gayole.herokuapp.com

Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@
},
"homepage": "https://github.com/fierfeu/Gayole#readme",
"devDependencies": {
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chromedriver": "^85.0.0",
"chai": "*",
"chai-as-promised": "*",
"chromedriver": "^85.0.1",
"coveralls": "^3.1.0",
"driver": "^1.0.0",
"geckodriver": "^1.19.1",
"jsdom": "^16.3.0",
"mocha": "^7.2.0",
"geckodriver": "^1.20.0",
"jsdom": "^16.4.0",
"mocha": "^8.1.3",
"selenium-webdriver": "^4.0.0-alpha.7",
"sinon": "^9.0.2"
"sinon": "^9.0.3"
},
"dependencies": {
"npm": "^6.14.6",
"npm-registry-fetch": "^8.1.1"
"npm": "^6.14.8",
"npm-registry-fetch": "^8.1.4"
}
}
2 changes: 1 addition & 1 deletion src/Client/html/boardGame.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div id='dialogZone' class='dialogZone'>
<div class="PA warLetters"
<div id='PA' class="PA warLetters"
data-help="Gives you the number of action points available for this turn">
<span style="display:block;margin-top:5px">0</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/Client/json/scenario_default.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
}
},
"conditions": {
"roundNb":6,
"turnNb":6,
"returnZone":["Siwa"],
"victoryTest":"()=>{return true}"
}
Expand Down
6 changes: 6 additions & 0 deletions src/Client/mjs/Didacticiel.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default class Didacticiel {
constructor (file) {
if(!file) throw 'ERROR No file to create a didacticiel sequence';

}
}
51 changes: 46 additions & 5 deletions src/Client/mjs/QOG.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import eventStorageInterface from './eventStorageInterface.mjs';
import unit from './unit.mjs';
import {unitSet} from './unitSet.mjs';
import zone from './zone.mjs';
import Scenario from './scenario.mjs';



export default class QOG {
constructor () {
Expand All @@ -27,11 +23,55 @@ export default class QOG {
QOG.prototype.scenarioParser(JSON.parse(data),this);
QOG.prototype.initScenario.call(this,this.currentScenario);
let turn = document.getElementById('turn').getElementsByTagName('span')[0];
turn.innerHTML = this.currentScenario.conditions.roundNb;
turn.innerHTML = this.currentScenario.conditions.turnNb;
this.currentGame.turnLeft = this.currentScenario.conditions.turnNb;
QOG.prototype.initGameEvent();
QOG.prototype.run.call(this); // for #37 bug
}).catch((err)=>{console.log(err)});
}

didacticiel () {
if(!this.currentScenario) throw 'ERROR no Scenario declared';
if(this.currentScenario.didacticiel.used) {
this.didacticiel= new Didacticiel(this.currentScenario.didacticiel.file);
}

}

run() {
if(!(this.units instanceof Object)) throw 'ERROR no units to let game running'
if(!(this.zones instanceof Object)) throw 'ERROR no zones to let game running'
if(!(this.hasOwnProperty('currentScenario'))) throw 'ERROR no scenario to let game running';
window.localStorage.setItem('gameLaunched',this.currentGame.name);
this.currentGame.turnLeft --;
this.currentGame.patrolNb=0;
for (let id in this.units) {
console.log(id);
if (this.units[id] instanceof unitSet) {
this.currentGame.patrolNb ++;
const nbOfUnitInPatrol = this.units[id].getNbOfUnitsInPatrol();
console.log(nbOfUnitInPatrol);
if (nbOfUnitInPatrol>1 && nbOfUnitInPatrol<3) {
this.units[id].actionPoints = 3+ Math.random()*5;
}
else if (nbOfUnitInPatrol>4 && nbOfUnitInPatrol<8) {
this.units[id].actionPoints = 4+ Math.random()*5 +Math.random()*5;
}
else {
this.units[id].actionPoints = 5+ Math.random()*5+Math.random()*5+Math.random()*5;
}
console.log(this.units[id].actionPoints);
const el =document.getElementById('PA').getElementsByTagName('span')[0];
el.innerHTML=this.units[id].actionPoints;
}
}
console.log('Run ok');

}

getGameName () {
return 'QOG';
}

initZones (gameManager) {
QOG.prototype.zones={};
Expand Down Expand Up @@ -257,4 +297,5 @@ export default class QOG {
}

import {Parser, parserOpponentDef} from './QOG_Parser.mjs';
import Didacticiel from './Didacticiel.mjs';
QOG.prototype.scenarioParser = Parser;
2 changes: 1 addition & 1 deletion src/Client/mjs/QOG_Parser.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import scenario from './scenario.mjs';

const parserDef =[["name"],["LRDG","Axis"],["roundNb","returnZone"]];
const parserDef =[["name"],["LRDG","Axis"],["turnNb","returnZone"]];
const OPPONENT_NAME = 1;
const VICOND =2;
export const parserOpponentDef = ["units","detachments","patrols","localisations"];
Expand Down
24 changes: 10 additions & 14 deletions src/Client/mjs/game.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export default class Game {
#sequence
#eventStorageInterface
constructor () {
if(globalThis.gameManager) throw('ERROR gameManager singleton allready created');
globalThis.gameManager = this;
this.#sequence =['boards','setUp','run']
//this.#sequence =['boards','setUp','run'] to repare bug #37
this.#sequence =['boards','setUp']; // #37 bug to remove run call by game
this.currentGame = {};
this.currentScenario=[];
}
Expand Down Expand Up @@ -35,18 +35,14 @@ export default class Game {

create(gameInterface) {
if(!(typeof gameInterface === 'function')) throw ('ERROR Bad Game interface provided : expect a class constructor and received a '+typeof gameInterface);
if(!gameInterface.prototype[this.#sequence[0]]) throw ('ERROR BAD Game interface in '+ gameInterface.name+' : '+this.#sequence[0]+' not available');
if (!(typeof gameInterface.prototype[this.#sequence[0]] === 'function')) throw ('ERROR BAD Game interface in '+ gameInterface.name+' : '+this.#sequence[0]+' is not a function');
gameInterface.prototype[this.#sequence[0]].call(this);

if(!gameInterface.prototype[this.#sequence[1]]) throw ('ERROR BAD Game interface in '+ gameInterface.name+' : '+this.#sequence[1]+' not available');
if (!(typeof gameInterface.prototype[this.#sequence[1]] === 'function')) throw ('ERROR BAD Game interface in '+ gameInterface.name+' : '+this.#sequence[0]+' is not a function');
gameInterface.prototype[this.#sequence[1]].call(this);

if(gameInterface.prototype.getGameName) this.currentGame.name= gameInterface.prototype.getGameName();
if(gameInterface.prototype.hasOwnProperty('getGameName')) this.currentGame.name= gameInterface.prototype.getGameName();
for(let i=0;i<this.#sequence.length;i++) {
if(!gameInterface.prototype[this.#sequence[i]]) throw ('ERROR BAD Game interface in '+ gameInterface.name+' : '+this.#sequence[i]+' not available');
if (!(typeof gameInterface.prototype[this.#sequence[i]] === 'function')) throw ('ERROR BAD Game interface in '+ gameInterface.name+' : '+this.#sequence[i]+' is not a function');
gameInterface.prototype[this.#sequence[i]].call(this);
}

if (window) window.localStorage.setItem('gameLaunched','QOG');
}
};


}
};
5 changes: 5 additions & 0 deletions src/Client/mjs/unitSet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ export class unitSet extends unit {
});
return nbOfUnitDetached;
}

getNbOfUnitsInPatrol () {
const entities = Object.keys(this.units);
return entities.length;
}
}
1 change: 1 addition & 0 deletions src/Servers/App/webHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const sitePagesConf ={
'/zone.mjs' : 'src/Client/mjs/zone.mjs',
'/game.mjs' : 'src/Client/mjs/game.mjs',
'/scenario.mjs' : 'src/Client/mjs/scenario.mjs',
'/Didacticiel.mjs':'src/Client/mjs/Didacticiel.mjs',
'/eventStorageInterface.mjs':'src/Client/mjs/eventStorageInterface.mjs',
'/eventManager.mjs':'src/Client/mjs/eventManager.mjs',
'/scenario_default.json':'src/Client/json/scenario_default.json',
Expand Down
6 changes: 5 additions & 1 deletion test/.mocharc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"noPackage":true,
"timeout": 5000,
"timeout": 10000,
"extension": [
"js",
"mjs"
Expand All @@ -17,10 +17,14 @@
"test/UnitTests/mjs/class_unit_test.mjs",
"test/UnitTests/mjs/class_zone_test.mjs",
"test/UnitTests/mjs/Class_Menu_test.mjs",
"test/UnitTests/mjs/class_Didacticiel_test.mjs",
"test/UnitTests/mjs/eventManager_test.mjs",
"test/UnitTests/mjs/Module_Interfaces_test.mjs",
"test/UnitTests/mjs/game_test.mjs",
"test/UnitTests/mjs/QOG_test.mjs",
"test/UnitTests/mjs/QOG_Didacticiel_tests.mjs",
"test/UnitTests/mjs/QOG_Parser_test.mjs",
"test/UnitTests/mjs/QOG_Run_tests.mjs",
"test/E2E/setUp.js",
"test/E2E/e2e-canary.js",
"test/E2E/MainPage-test.js",
Expand Down
11 changes: 6 additions & 5 deletions test/E2E/MainPage-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ describe('[Main Page overall Tests]',()=>{
});

it('and has good link to load index.css', async ()=>{
let cssLink = await browser.findElement(By.tagName('link'));
let cssLink = await browser.findElement(By.css('link'));
expect(await cssLink.getAttribute('href')).to.include('/index.css');

});

it('has good script links and async to load main js', async ()=>{
let jsScript = await browser.findElement(By.tagName('script'));
let jsScript = await browser.findElement(By.css('script'));
expect(await jsScript.getAttribute('src')).to.include('/QOG_Head.js');
expect(await jsScript.getAttribute('async')).to.equal('true');
expect(await jsScript.getAttribute('type')).to.equal('module');
Expand All @@ -40,6 +40,7 @@ describe('[Main Page overall Tests]',()=>{
// if I use promise in the following line, it'll resolved after the click()
expect(await mainMenu.getAttribute('class')).to.equal('minifiedMainMenu');
await mainMenu.click();
console.log(await mainMenu.getAttribute('class'));
expect(await mainMenu.getAttribute('class')).to.equal('minifiedMainMenu maxifiedMainMenu');
await mainMenu.click();
expect(await mainMenu.getAttribute('class')).to.equal('minifiedMainMenu');
Expand All @@ -51,7 +52,7 @@ describe('[Main Page overall Tests]',()=>{
let mainMenu = browser.findElement(By.css('#mainMenu'));
await mainMenu.click();
const buttonList = await browser.findElement(By.css('#buttonList'));
const buttons = await buttonList.findElements(By.tagName('button'));
const buttons = await buttonList.findElements(By.css('button'));
expect (buttons.length).to.equal(3);
//Saved button should be the third one
expect (await buttons[2].getCssValue('cursor')).to.equal('not-allowed'); // to remove as soon
Expand All @@ -61,7 +62,7 @@ describe('[Main Page overall Tests]',()=>{

it('game loading button is unavailable for the moment', async () => {
const buttonList = await browser.findElement(By.css('#buttonList'));
const buttons = await buttonList.findElements(By.tagName('button'));
const buttons = await buttonList.findElements(By.css('button'));
//Laoding button should be the second one and is unavailable until user management added
expect (await buttons[1].getCssValue('cursor')).to.equal('not-allowed');
expect (await buttons[1].getCssValue('opacity')).to.equal('0.5');
Expand All @@ -70,7 +71,7 @@ describe('[Main Page overall Tests]',()=>{

it('game creation button available and in first place', async ()=>{
const buttonList = await browser.findElement(By.css('#buttonList'));
const buttons = await buttonList.findElements(By.tagName('button'));
const buttons = await buttonList.findElements(By.css('button'));
//create button is the first one
expect(await buttons[0].getText()).to.equal('CREER NOUVELLE PARTIE');
expect(await buttons[0].getAttribute('class')).to.equal('btn-enabled');
Expand Down
Loading

0 comments on commit 7d3f819

Please sign in to comment.