Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Introduce deck builder for integration tests (#496)
Browse files Browse the repository at this point in the history
* Add submodule for thronesdb-json-data.

* Convert existing tests to use the deck builder.

* Add deck builder helper class for tests

* Update CircleCI config to support submodules.

* Update player interaction to accept card pack
  • Loading branch information
ystros authored and cryogen committed Feb 27, 2017
1 parent 2e66b08 commit 916aa0d
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "thronesdb-json-data"]
path = thronesdb-json-data
url = https://github.com/Alsciende/thronesdb-json-data.git
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ The game uses mongodb as storage so you'll need that installed and running

```
Clone the repository
git submodule init
git submodule update
Run npm install
mkdir server/logs
cd server
Expand Down
4 changes: 4 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
checkout:
post:
- git submodule sync
- git submodule update --init
machine:
node:
version: 6.1.0
Expand Down
17 changes: 17 additions & 0 deletions test/helpers/cardutil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const CardUtil = {
matchCardByNameAndPack(labelOrName) {
var name = labelOrName;
var pack;
var match = labelOrName.match(/^(.*)\s\((.*)\)$/);
if(match) {
name = match[1];
pack = match[2];
}

return function(cardData) {
return cardData.name === name && (!pack || cardData.pack_code === pack);
};
}
};

module.exports = CardUtil;
72 changes: 72 additions & 0 deletions test/helpers/deckbuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const fs = require('fs');
const path = require('path');
const _ = require('underscore');

const {matchCardByNameAndPack} = require('./cardutil.js');

const PathToSubModulePacks = path.join(__dirname, '../../thronesdb-json-data/pack');

class DeckBuilder {
constructor() {
this.cards = this.loadCards(PathToSubModulePacks);
}

loadCards(directory) {
var cards = {};

var jsonPacks = fs.readdirSync(directory).filter(file => file.endsWith('.json'));

_.each(jsonPacks, file => {
var cardsInPack = require(path.join(PathToSubModulePacks, file));

_.each(cardsInPack, card => {
cards[card.code] = card;
});
});

return cards;
}

buildDeck(faction, cardLabels) {
var cardCounts = {};
_.each(cardLabels, label => {
var cardData = this.getCard(label);
if(cardCounts[cardData.code]) {
cardCounts[cardData.code].count++;
} else {
cardCounts[cardData.code] = {
count: 1,
card: cardData
};
}
});

return {
faction: { value: faction },
agenda: _.find(cardCounts, cardCount => cardCount.card.type_code === 'agenda'),
drawCards: _.filter(cardCounts, cardCount => ['character', 'location', 'attachment', 'event'].includes(cardCount.card.type_code)),
plotCards: _.filter(cardCounts, cardCount => cardCount.card.type_code === 'plot')
};
}

getCard(codeOrLabelOrName) {
if(this.cards[codeOrLabelOrName]) {
return this.cards[codeOrLabelOrName];
}

var cardsByName = _.filter(this.cards, matchCardByNameAndPack(codeOrLabelOrName));

if(cardsByName.length === 0) {
throw new Error(`Unable to find any card matching ${codeOrLabelOrName}`);
}

if(cardsByName.length > 1) {
var matchingLabels = _.map(cardsByName, card => `${card.name} (${card.pack_code})`).join('\n');
throw new Error(`Multiple cards match the name ${codeOrLabelOrName}. Use one of these instead:\n${matchingLabels}`);
}

return cardsByName[0];
}
}

module.exports = DeckBuilder;
7 changes: 7 additions & 0 deletions test/helpers/integrationhelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const _ = require('underscore');

const DeckBuilder = require('./deckbuilder.js');
const GameFlowWrapper = require('./gameflowwrapper.js');

const ProxiedGameFlowWrapperMethods = [
Expand All @@ -11,6 +12,8 @@ const ProxiedGameFlowWrapperMethods = [
'completeTaxationPhase', 'selectPlotOrder', 'completeSetup'
];

const deckBuilder = new DeckBuilder();

global.integration = function(definitions) {
describe('integration', function() {
beforeEach(function() {
Expand All @@ -25,6 +28,10 @@ global.integration = function(definitions) {
_.each(ProxiedGameFlowWrapperMethods, method => {
this[method] = (...args) => this.flow[method].apply(this.flow, args);
});

this.buildDeck = function(faction, cards) {
return deckBuilder.buildDeck(faction, cards);
};
});

definitions();
Expand Down
5 changes: 4 additions & 1 deletion test/helpers/playerinteractionwrapper.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const _ = require('underscore');

const {matchCardByNameAndPack} = require('./cardutil.js');

class PlayerInteractionWrapper {
constructor(game, player) {
this.game = game;
Expand Down Expand Up @@ -33,7 +35,8 @@ class PlayerInteractionWrapper {
}

filterCardsByName(name) {
var cards = this.player.allCards.filter(card => card.name === name);
var matchFunc = matchCardByNameAndPack(name);
var cards = this.player.allCards.filter(card => matchFunc(card.cardData));

if(cards.length === 0) {
throw new Error(`Could not find any matching card "${name}" for ${this.player.name}`);
Expand Down
14 changes: 2 additions & 12 deletions test/server/cards/locations/01/01137 - thewall.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,16 @@

describe('TheWall', function() {
integration(function() {
const wallCardData = { 'pack_code' : 'Core', 'pack_name' : 'Core Set', 'type_code' : 'location', 'type_name' : 'Location', 'faction_code' : 'thenightswatch', 'faction_name' : 'The Night\'s Watch', 'position' : 137, 'code' : '01137', 'name' : 'The Wall', 'cost' : 4, 'text' : 'Each [thenightswatch] character you control gets +1 STR.\n<b>Forced Reaction:</b> After you lose an unopposed challenge, kneel The Wall.\n<b>Interrupt:</b> When the challenges phase ends, kneel The Wall to gain 2 power for your faction.', 'quantity' : 1, 'income' : null, 'initiative' : null, 'claim' : null, 'reserve' : null, 'deck_limit' : 3, 'strength' : null, 'traits' : 'Stronghold. The North.', 'flavor' : null, 'illustrator' : 'Lino Drieghe', 'is_unique' : true, 'is_loyal' : false, 'is_military' : false, 'is_intrigue' : false, 'is_power' : false, 'octgn_id' : '5d20e021-5d12-4338-8bdd-42d008bff919', 'url' : 'https://thronesdb.com/card/01137', 'imagesrc' : '/bundles/cards/01137.png', 'label' : 'The Wall', 'ci' : 4, 'si' : -1 };

const deck = {
faction: { value: 'thenightswatch' },
drawCards: [
{ count: 2, card: wallCardData },
{ count: 1, card: { faction_code: 'thenightswatch', name: 'Test Character', type_code: 'character', strength: 1, cost: 0 } }
],
plotCards: []
};

describe('when dupes are put out in the setup phase', function() {
beforeEach(function() {
const deck = this.buildDeck('thenightswatch', ['The Wall', 'The Wall', 'Steward at the Wall']);
this.player1.selectDeck(deck);
this.player2.selectDeck(deck);
this.startGame();
this.keepStartingHands();

[this.wall1, this.wall2] = this.player1.filterCardsByName('The Wall');
this.character = this.player1.findCardByName('Test Character');
this.character = this.player1.findCardByName('Steward at the Wall');
});

it('should not count duplicates toward character strength', function() {
Expand Down
24 changes: 3 additions & 21 deletions test/server/cards/plots/04/04039 - summerharvest.spec.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,11 @@
/* global describe, it, expect, beforeEach, integration */
/* eslint camelcase: 0, no-invalid-this: 0, quotes: 0 */
/* eslint camelcase: 0, no-invalid-this: 0 */

describe('SummerHarvest', function() {
integration(function() {
const summerHarvestCardData = { "pack_code" : "CtA", "pack_name" : "Called to Arms", "type_code" : "plot", "type_name" : "Plot", "faction_code" : "neutral", "faction_name" : "Neutral", "position" : 39, "code" : "04039", "name" : "Summer Harvest", "cost" : null, "text" : "<b>When Revealed:</b> Choose an opponent. X is 2 higher than the printed gold value on that player's revealed plot card.", "quantity" : 3, "income" : 0, "initiative" : 4, "claim" : 1, "reserve" : 6, "deck_limit" : 2, "strength" : null, "traits" : "Summer.", "flavor" : null, "illustrator" : "Tomasz Jedruszek", "is_unique" : false, "is_loyal" : false, "is_military" : false, "is_intrigue" : false, "is_power" : false, "octgn_id" : "9804cffd-3269-4860-8285-135446dd3dba", "url" : "https://thronesdb.com/card/04039", "imagesrc" : "/bundles/cards/04039.png", "label" : "Summer Harvest", "ci" : null, "si" : 4 };
const nobleCauseCardData = { "pack_code" : "Core", "pack_name" : "Core Set", "type_code" : "plot", "type_name" : "Plot", "faction_code" : "neutral", "faction_name" : "Neutral", "position" : 4, "code" : "01004", "name" : "A Noble Cause", "cost" : null, "text" : "Reduce the cost of the first <i>Lord</i> or <i>Lady</i> character you marshal this round by 2.", "quantity" : 1, "income" : 5, "initiative" : 0, "claim" : 1, "reserve" : 6, "deck_limit" : 2, "strength" : null, "traits" : "Kingdom. Noble.", "flavor" : null, "illustrator" : "Drazenka Kimpel", "is_unique" : false, "is_loyal" : false, "is_military" : false, "is_intrigue" : false, "is_power" : false, "octgn_id" : "0fb3ad4b-5cf3-49e9-a33f-484ecafeabf8", "url" : "https://thronesdb.com/card/01004", "imagesrc" : "/bundles/cards/01004.png", "label" : "A Noble Cause", "ci" : 5, "si" : null };
const varysRiddleCardData = { "pack_code" : "AtSK", "pack_name" : "Across the Seven Kingdoms", "type_code" : "plot", "type_name" : "Plot", "faction_code" : "neutral", "faction_name" : "Neutral", "position" : 20, "code" : "04020", "name" : "Varys's Riddle", "cost" : null, "text" : "<b>When Revealed:</b> Initiate the when revealed ability on a revealed non-<i>Riddle</i> plot card as if you had just revealed it.", "quantity" : 3, "income" : 5, "initiative" : 6, "claim" : 1, "reserve" : 7, "deck_limit" : 2, "strength" : null, "traits" : "Riddle. Scheme.", "flavor" : "\"In a room sit three great men, a king, a priest, and a rich man with his gold. Between them stands a sellsword, a little man of common birth and no great mind. Each of the great ones bids him slay the other two.\" <cite>Varys</cite>", "illustrator" : "Serena Malyon", "is_unique" : false, "is_loyal" : false, "is_military" : false, "is_intrigue" : false, "is_power" : false, "octgn_id" : "d68d7083-089d-4d28-8249-e70613639680", "url" : "https://thronesdb.com/card/04020", "imagesrc" : "/bundles/cards/04020.png", "label" : "Varys's Riddle", "ci" : 5, "si" : 6 };

const deck1 = {
faction: { value: 'lannister' },
plotCards: [
{ count: 1, card: summerHarvestCardData }
],
drawCards: []
};
const deck2 = {
faction: { value: 'lannister' },
plotCards: [
{ count: 1, card: nobleCauseCardData },
{ count: 1, card: varysRiddleCardData }
],
drawCards: []
};

beforeEach(function() {
const deck1 = this.buildDeck('lannister', ['Summer Harvest', 'Tyrion Lannister (Core)']);
const deck2 = this.buildDeck('lannister', ['A Noble Cause', 'Varys\'s Riddle']);
this.player = this.player1Object;
this.opponent = this.player2Object;

Expand Down
1 change: 1 addition & 0 deletions thronesdb-json-data
Submodule thronesdb-json-data added at 19ae33

0 comments on commit 916aa0d

Please sign in to comment.