From 717b5324bf977eabbaf0d26f3c4c22b2432a6870 Mon Sep 17 00:00:00 2001 From: Alec Maier Date: Wed, 29 Apr 2020 22:56:23 -0400 Subject: [PATCH] start of e2e tests --- browsertest.js | 8 ++- package.json | 6 ++- server.js | 2 +- spec/SpecHelper.js | 93 ++++++++++++++++++++++++++++++++ spec/e2e/SetupSpec.js | 37 +++++++++++++ spec/support/jasmine.json | 4 +- static/modules/logger.js | 20 +++++++ static/setup.js | 109 ++++++++++++++++++++------------------ views/SpecRunner.html | 4 +- views/create_game.html | 6 ++- 10 files changed, 225 insertions(+), 64 deletions(-) create mode 100644 spec/SpecHelper.js create mode 100644 spec/e2e/SetupSpec.js create mode 100644 static/modules/logger.js diff --git a/browsertest.js b/browsertest.js index 39d0842..3500adc 100644 --- a/browsertest.js +++ b/browsertest.js @@ -24,11 +24,9 @@ const port = Array app.set("port", port); app.use(express.static(__dirname)); -app.use(express.static(path.join(__dirname, 'images'))); -app.use(express.static(path.join(__dirname, 'fonts'))); +app.use(express.static(path.join(__dirname, 'assets'))); +app.use(express.static(path.join(__dirname, 'static'))); app.use(express.static(path.join(__dirname, 'views'))); -app.use(express.static(path.join(__dirname, 'scripts'))); -app.use(express.static(path.join(__dirname, 'stylesheets'))); app.use(express.static(path.join(__dirname, 'spec'))); app.get("/tests",function(request, response){ @@ -67,4 +65,4 @@ if(openBrowser) { } else { open("http://localhost:" + port + "/tests"); } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 7700468..9d4a092 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,10 @@ "description": "", "main": "index.js", "scripts": { - "test": "jasmine", - "test:all": "jasmine && node browsertest.js openBrowser socket" + "start": "node server.js", + "test": "jasmine && node browsertest.js openBrowser socket", + "test:unit": "jasmine", + "test:e2e": "node browsertest.js" }, "author": "", "license": "ISC", diff --git a/server.js b/server.js index 8dd2756..74e173c 100644 --- a/server.js +++ b/server.js @@ -5,7 +5,7 @@ const socketIO = require('socket.io'); const app = express(); const server = http.Server(app); const io = socketIO(server); -const ServerHelper = require('server-helper.js'); +const ServerHelper = require('./server-helper'); const secure = require('express-force-https'); app.use(secure); diff --git a/spec/SpecHelper.js b/spec/SpecHelper.js new file mode 100644 index 0000000..bc046a8 --- /dev/null +++ b/spec/SpecHelper.js @@ -0,0 +1,93 @@ +let socket; + +// Jasmine lifecycle hooks + +beforeEach(() => { + cleanupPlayground(); + // jasmine.addMatchers({ /* provide matcher function here if you want to, I guess */}); +}); + +beforeAll(() => { + if(window.location.search.includes("socket=true")) { + socket = io(); + } +}); + +afterEach(() => { + /* + if(socket !== undefined) { + socket.emit("test result",parseTestResults()); + } + + How to get only the newest result? No clue right now. + + */ +}); + +afterAll(() => { + resportResultsOnSocket(); +}); + + +// Utility functions + +const parseTestResults = function() { + const resultsElement = document.querySelector("span.jasmine-overall-result.jasmine-bar"); + + if(resultsElement !== null) { + const results = /.*(\d+) specs.*(\d+) failure.*(\d+).*/.exec(resultsElement.textContent); + const total = parseInt(results[1]); + const failures = parseInt(results[2]); + const pending = parseInt(results[3]); + return Promise.resolve({ + "totalCount": total, + "failureCount": failures, + "pendingCount": pending + }); + } else { + let maxRetries = 60; + return new Promise((resolve, reject) => { + const resultsInterval = setInterval(() => { + maxRetries--; + const resultsElem = document.querySelector("span.jasmine-overall-result.jasmine-bar"); + + if (maxRetries === 0) { + reject("Required results element not found."); + } else { + if (resultsElem !== null) { + const results = /.*(\d+) spec.*(\d+) failure.*(\d+).*/.exec(resultsElem.textContent); + const pendingResults = /.*(\d+) pending.*/.exec(resultsElem.textContent); + const total = parseInt(results[1]); + const failures = parseInt(results[2]); + const pending = pendingResults ? parseInt(pendingResults[1]) : 0; + clearInterval(resultsInterval); + resolve({ + "totalCount": total, + "failureCount": failures, + "pendingCount": pending + }); + } + } + }, 500); + }); + } +}; + +export const addStubElement = function(element) { + document.getElementById("playground").appendChild(element); +}; + +export const cleanupPlayground = function() { + document.getElementById("playground").innerHTML = ""; +}; + +export const resportResultsOnSocket = function() { + + if(socket === undefined) { + socket = io(); + } + + parseTestResults().then((results) => { + socket.emit("all-results",results); + }).catch((e) => {console.error(e);}); +}; diff --git a/spec/e2e/SetupSpec.js b/spec/e2e/SetupSpec.js new file mode 100644 index 0000000..3ad16b2 --- /dev/null +++ b/spec/e2e/SetupSpec.js @@ -0,0 +1,37 @@ +import { addStubElement } from "../SpecHelper.js" +import { setup } from "../../static/setup.js" + +describe("Home page", function() { + + beforeEach(function() { + + }); + + it("should render the set of standard and custom roles", function() { + + // arrange + let goodCards = document.createElement("div"); + goodCards.setAttribute("id", "card-select-good"); + + let evilCards = document.createElement("div"); + evilCards.setAttribute("id", "card-select-evil"); + + let roles = document.createElement("div"); + roles.setAttribute("id", "roles"); + + let customRoles = document.createElement("div"); + customRoles.setAttribute("id", "custom-roles"); + + addStubElement(goodCards); + addStubElement(evilCards); + addStubElement(roles); + addStubElement(customRoles); + + // act + setup.renderAvailableCards(false); + + //assert + expect(document.getElementById("card-0")).toBeDefined(); + }); + +}); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index f004bf2..10716bd 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -1,5 +1,5 @@ { - "spec_dir": "spec/unit", + "spec_dir": "spec/unit/", "spec_files": [ "**/*[sS]pec.js" ], @@ -8,4 +8,4 @@ ], "stopSpecOnExpectationFailure": false, "random": true -} \ No newline at end of file +} diff --git a/static/modules/logger.js b/static/modules/logger.js new file mode 100644 index 0000000..c49a1f4 --- /dev/null +++ b/static/modules/logger.js @@ -0,0 +1,20 @@ +module.exports = function(debugMode = false){ + return { + log(message = "") { + const now = new Date(); + console.log('LOG ', now.toGMTString(), ': ', message); + }, + + debug(message = "") { + if (!debugMode) return; + const now = new Date(); + console.debug('DEBUG ', now.toGMTString(), ': ', message); + }, + + error(message = "") { + if (!debugMode) return; + const now = new Date(); + console.error('ERROR ', now.toGMTString(), ': ', message); + } + }; +}; diff --git a/static/setup.js b/static/setup.js index 7dd1766..e6df4ed 100644 --- a/static/setup.js +++ b/static/setup.js @@ -21,57 +21,64 @@ const fullDeck = []; let gameSize = 0; let atLeastOnePlayer = false; -// register event listeners on buttons -document.getElementById("reset-btn").addEventListener("click", resetCardQuantities); -document.getElementById("create-btn").addEventListener("click", createGame); -document.getElementById("role-view-changer-gallery").addEventListener("click", function() { toggleViewChanger(false) }); -document.getElementById("role-view-changer-list").addEventListener("click", function() { toggleViewChanger(true) }); -document.getElementById("role-btn").addEventListener("click", function() { displayModal("role-modal", undefined) }); -document.getElementById("edit-role-btn").addEventListener("click", function() { displayModal("edit-custom-roles-modal", undefined) }); -document.getElementById("custom-role-form").addEventListener("submit", function(e) { - addCustomCardToRoles(e); -}); -Array.from(document.getElementsByClassName("close")).forEach(function(element) { - element.addEventListener('click', closeModal); -}); +export class Setup { + constructor() { + // render all of the available cards to the user + window.onload = function() { + // register event listeners on buttons + document.getElementById("reset-btn").addEventListener("click", resetCardQuantities); + document.getElementById("create-btn").addEventListener("click", createGame); + document.getElementById("role-view-changer-gallery").addEventListener("click", function() { toggleViewChanger(false) }); + document.getElementById("role-view-changer-list").addEventListener("click", function() { toggleViewChanger(true) }); + document.getElementById("role-btn").addEventListener("click", function() { displayModal("role-modal", undefined) }); + document.getElementById("edit-role-btn").addEventListener("click", function() { displayModal("edit-custom-roles-modal", undefined) }); + document.getElementById("custom-role-form").addEventListener("submit", function(e) { + addCustomCardToRoles(e); + }); + Array.from(document.getElementsByClassName("close")).forEach(function(element) { + element.addEventListener('click', closeModal); + }); -// render all of the available cards to the user -window.onload = function() { - readInUserCustomRoles(); - renderAvailableCards(false); -}; - -function renderAvailableCards(isCondensed) { - cards.sort(function(a, b) { - return a.role.toUpperCase().localeCompare(b.role); - }); - document.getElementById("card-select-good").innerHTML = ""; - document.getElementById("card-select-evil").innerHTML = ""; - document.getElementById("roles").innerHTML = ""; - document.getElementById("custom-roles").innerHTML = ""; - - for (let i = 0; i < cards.length; i ++) { - cards[i].team === "good" - ? renderGoodRole(cards[i], i, isCondensed) - : renderEvilRole(cards[i], i, isCondensed); + readInUserCustomRoles(); + setup.renderAvailableCards(false); + }; } - - if (document.getElementById("custom-roles").getElementsByClassName("custom-role-edit").length === 0) { - document.getElementById("custom-roles").innerHTML = "

You haven't added any custom cards.

"; - } - - let customCardGood = CardManager.constructCustomCardIndicator(isCondensed, "good"); - let customCardEvil = CardManager.constructCustomCardIndicator(isCondensed, "evil"); - document.getElementById("card-select-good").appendChild(customCardGood); - document.getElementById("card-select-evil").appendChild(customCardEvil); - customCardGood.addEventListener("click", function() { - displayModal("custom-card-modal", "Good"); - }); - customCardEvil.addEventListener("click", function() { - displayModal("custom-card-modal", "Evil"); - }); } +export const setup = { + + renderAvailableCards(isCondensed) { + cards.sort(function(a, b) { + return a.role.toUpperCase().localeCompare(b.role); + }); + document.getElementById("card-select-good").innerHTML = ""; + document.getElementById("card-select-evil").innerHTML = ""; + document.getElementById("roles").innerHTML = ""; + document.getElementById("custom-roles").innerHTML = ""; + + for (let i = 0; i < cards.length; i ++) { + cards[i].team === "good" + ? renderGoodRole(cards[i], i, isCondensed) + : renderEvilRole(cards[i], i, isCondensed); + } + + if (document.getElementById("custom-roles").getElementsByClassName("custom-role-edit").length === 0) { + document.getElementById("custom-roles").innerHTML = "

You haven't added any custom cards.

"; + } + + let customCardGood = CardManager.constructCustomCardIndicator(isCondensed, "good"); + let customCardEvil = CardManager.constructCustomCardIndicator(isCondensed, "evil"); + document.getElementById("card-select-good").appendChild(customCardGood); + document.getElementById("card-select-evil").appendChild(customCardEvil); + customCardGood.addEventListener("click", function() { + displayModal("custom-card-modal", "Good"); + }); + customCardEvil.addEventListener("click", function() { + displayModal("custom-card-modal", "Evil"); + }); + } +}; + function renderGoodRole(cardInfo, i, isCondensed) { const card = CardManager.createCard(cardInfo); if (card.custom) { @@ -152,7 +159,7 @@ function addCustomCardToRoles(e) { saved: document.getElementById("custom-role-remember").checked }; cards.push(newCard); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); + setup.renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); if (newCard.saved === true) { let existingRoles = localStorage.getItem("play-werewolf-custom-roles"); @@ -250,7 +257,7 @@ function toggleViewChanger(isCondensed) { document.getElementById("role-view-changer-gallery").classList.add("selected"); document.getElementById("role-view-changer-list").classList.remove("selected"); } - renderAvailableCards(isCondensed); + setup.renderAvailableCards(isCondensed); } function buildRoleEditForm(index) { @@ -300,7 +307,7 @@ function removeCustomRole(name) { localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles)); } updateCustomRoleModal(); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); + setup.renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); } } @@ -315,7 +322,7 @@ function updateCustomRole(event, index) { removeOrAddSavedRoleIfNeeded(cardToUpdate); toggleEditForm(event, index); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); + setup.renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); } } diff --git a/views/SpecRunner.html b/views/SpecRunner.html index 8f82a52..f87ecd6 100644 --- a/views/SpecRunner.html +++ b/views/SpecRunner.html @@ -17,8 +17,8 @@ - - + +