index : btroops | |
Virtual board game-aid for BattleTroops, an infantry combat simulator wargame published by FASA in 1989. |
aboutsummaryrefslogtreecommitdiff |
diff options
-rw-r--r-- | jest.config.integ.cjs | 5 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | public/index.html | 4 | ||||
-rw-r--r-- | test/integration/fixtures/scenario.svg (renamed from test/integration/fixtures/scenario-test.svg) | 8 | ||||
-rw-r--r-- | test/integration/helpers.cjs | 72 | ||||
-rw-r--r-- | test/integration/select.test.js | 172 |
6 files changed, 156 insertions, 107 deletions
diff --git a/jest.config.integ.cjs b/jest.config.integ.cjs index 28562ea..083cbd7 100644 --- a/jest.config.integ.cjs +++ b/jest.config.integ.cjs @@ -5,8 +5,9 @@ module.exports = { globalTeardown: './test/integration/teardown.cjs', setupFiles: ['./test/integration/helpers.cjs'], testPathIgnorePatterns: ['/node_modules/', 'test/unit'], - testTimeout: 5000, + verbose: true, + randomize: true, globals: { testServerUrl: 'http://localhost:3005/' - } + }, }; diff --git a/package.json b/package.json index 620fc02..9c26357 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "scripts": { "start": "node server.cjs", "start:esbuildserver": "node esbuild-server.mjs", - "test:integ": "node --trace-warnings ./node_modules/.bin/jest --detectOpenHandles --config jest.config.integ.cjs", + "test:integ": "node ./node_modules/.bin/jest --detectOpenHandles --config jest.config.integ.cjs", "test:integ:debug": "NODE_INSPECT_RESUME_ON_START=1 node inspect ./node_modules/jest/bin/jest.js --config jest.config.integ.cjs --runInBand", "test": "jest" } diff --git a/public/index.html b/public/index.html index df3fc65..b78df5b 100644 --- a/public/index.html +++ b/public/index.html @@ -145,7 +145,7 @@ Loading... </div> - <object type="image/svg+xml" data="assets/images/scenario_template.svg"></object> + <object id="map-resource" type="image/svg+xml" data="assets/images/scenario_template.svg"></object> <div id="status"> <span id="hex-counter">Distance: <span id="hex-count">-</span></span> @@ -246,7 +246,7 @@ <script> const source = new EventSource('/esbuild'); source.addEventListener('change', () => location.reload()); - source.addEventListener('message', (e) => console.log(e)); + // source.addEventListener('message', (e) => console.log(e)); </script> <script src="index.js"></script> <script src="soldier_record_block.js"></script> diff --git a/test/integration/fixtures/scenario-test.svg b/test/integration/fixtures/scenario.svg index 77afd6b..e5b7121 100644 --- a/test/integration/fixtures/scenario-test.svg +++ b/test/integration/fixtures/scenario.svg @@ -9,13 +9,7 @@ </g> <g data-y="1"> <g data-x="0"><use href="#hex"/></g> - <g data-x="1"><use href="#hex"/> - <g class="counter" data-allegiance="attacker" data-number="1"> - <use class="primary-weapon" href="counters.svg#rifle"/> - <use class="troop-number" href="counters.svg#number-1"/> - <use class="squad-number" href="counters.svg#number-1"/> - </g> - </g> + <g data-x="1"><use href="#hex"/></g> <g data-x="2"><use href="#hex"/></g> </g> <g data-y="2"> diff --git a/test/integration/helpers.cjs b/test/integration/helpers.cjs index 7e3e0fd..d38f1b6 100644 --- a/test/integration/helpers.cjs +++ b/test/integration/helpers.cjs @@ -1,5 +1,8 @@ const { mkdir, writeFile } = require('node:fs/promises') , path = require('path') + , { By, until } = require('selenium-webdriver') + , { JSDOM } = require('jsdom') + , { HttpResponse } = require('selenium-webdriver/devtools/networkinterceptor') ; global.takeScreenshot = async (driver) => { @@ -14,3 +17,72 @@ global.url = (relativeOrAbsolute) => { const location = new URL(relativeOrAbsolute, global.testServerUrl); return location.href; } + +global.page = { + waitUntilMapLoaded: async function (driver) { + const mapPlaceholderEl = await driver.findElement(By.css('.map-placeholder')); + await driver.wait(until.elementIsNotVisible(mapPlaceholderEl), 1000); + }, + + findElInFrame: async function (frame, locator) { + const driver = frame.getDriver(); + await driver.switchTo().frame(frame); + return driver.findElement(locator); + }, + + expectSelected: async function (el) { + expect(await el.getAttribute('class')).toEqual(expect.stringContaining('selected')); + }, + + expectNotSelected: async function (el) { + expect(await el.getAttribute('class')).toEqual(expect.not.stringContaining('selected')); + }, + + troopCounterSelector: function ({ allegiance = 'attacker', troopNumber = 1 } = {}) { + return `.counter[data-allegiance="${allegiance}"][data-number="${troopNumber}"]`; + }, + + findMapResourceEl: function (driver) { + return driver.findElement(By.css('object')); + }, + + findTroopRecordEl: function (driver) { + return driver.findElement(By.css('.soldier-record')); + } +}; + +global.mockResponse = async function mockResponse(driver, urlPath, template, createFn) { + const connection = driver.createCDPConnection('page'); + const dom = new JSDOM(template); + const contents = createFn(dom.window.document); + + const httpResponse = new HttpResponse(url(urlPath)); + httpResponse.body = contents; + httpResponse.addHeaders('Content-Type', 'image/svg+xml'); + + await driver.onIntercept(await connection, httpResponse, async () => {}); +} + +global.createTroopCounter = function ( + allegiance = 'attacker', + troopNumber = 1, + squadNumber = 1, + weapon = 'rifle' +) { + return JSDOM.fragment(` + <g class="counter" data-allegiance="${allegiance}" data-number="${troopNumber}"> + <use class="primary-weapon" href="counters.svg#${weapon}"></use> + <use class="troop-number" href="counters.svg#number-${troopNumber}"></use> + <use class="squad-number" href="counters.svg#number-${squadNumber}"></use> + </g> + `); +} + +global.placeCounter = function (document, counter, { x, y }) { + document.querySelector(`g[data-y="${y}"] g[data-x="${x}"]`).append(counter); +} + +global.selectCounter = function (counter) { + counter.querySelector('.counter').classList.add('selected'); + return counter; +} diff --git a/test/integration/select.test.js b/test/integration/select.test.js index d9b632e..acde0f0 100644 --- a/test/integration/select.test.js +++ b/test/integration/select.test.js @@ -1,136 +1,118 @@ -const { Builder, By, until } = require('selenium-webdriver'), +const { Builder, By } = require('selenium-webdriver'), chrome = require('selenium-webdriver/chrome.js'), chromeOptions = new chrome.Options(), - { readFile, readdir } = require('node:fs/promises'), - { HttpResponse } = require('selenium-webdriver/devtools/networkinterceptor'); + { readFile, readdir } = require('node:fs/promises'); chromeOptions.addArguments('--headless', '--disable-gpu', '--no-sandbox'); const buildPath = 'build/assets/images'; const defaultScenario = 'scenario-side_show'; -const fixtureFilePath = './test/integration/fixtures/scenario-test.svg'; +const fixtureFilePath = './test/integration/fixtures/scenario.svg'; -const selected = expect.stringContaining('selected'); -const notSelected = expect.not.stringContaining('selected'); - -let driver, httpResponse; +let driver, fixture, scenario; beforeAll(async () => { - const filenames = await readdir(buildPath); - const scenario = filenames.find(filename => filename.includes(defaultScenario)); - - httpResponse = new HttpResponse(url(`/assets/images/${scenario}`)); - httpResponse.body = await readFile(fixtureFilePath, 'utf8'); - httpResponse.addHeaders('Content-Type', 'image/svg+xml'); + const dirread = readdir(buildPath); + const fileRead = readFile(fixtureFilePath, 'utf8'); + const filenames = await dirread; + scenario = filenames.find(filename => filename.includes(defaultScenario)); + fixture = await fileRead; }); beforeEach(async () => { driver = new Builder().forBrowser('chrome').setChromeOptions(chromeOptions).build(); - const connection = await driver.createCDPConnection('page') - await driver.onIntercept(connection, httpResponse, async () => {}); - await driver.get(url('/')); - const mapPlaceholderEl = await driver.findElement(By.css('.map-placeholder')); - await driver.wait(until.elementIsNotVisible(mapPlaceholderEl), 1000); }); -it('selects and deselects a trooper by clicking on its counter', async () => { - const record = await driver.findElement(By.css('.soldier-record')); - const objectEl = await driver.findElement(By.css('object')); - const selector = '.counter[data-allegiance="attacker"][data-number="1"]'; - - expect(await record.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().frame(objectEl); - const svg = await driver.findElement(By.css('svg')); - const counter = await driver.findElement(By.css(selector), svg); - expect(await counter.getAttribute('class')).toEqual(notSelected); +describe('a trooper', () => { + beforeEach(async () => { + await mockResponse(driver, `/assets/images/${scenario}`, fixture, (document) => { + placeCounter(document, createTroopCounter(), { x: 1, y: 1 }); + return `<?xml version="1.0" standalone="yes"?>\n` + document.querySelector('svg').outerHTML; + }); + }); - await counter.click(); + it('is selected by clicking on its counter', async () => { + await driver.get(url('/')); + await page.waitUntilMapLoaded(driver); - expect(await counter.getAttribute('class')).toEqual(selected); - await driver.switchTo().defaultContent(); - expect(await record.getAttribute('class')).toEqual(selected); + const record = page.findTroopRecordEl(driver); + const mapResource = page.findMapResourceEl(driver); - await driver.switchTo().frame(objectEl); - await counter.click(); + await page.expectNotSelected(await record); + const counter = await page.findElInFrame(await mapResource, By.css(page.troopCounterSelector())); + await page.expectNotSelected(counter); - expect(await counter.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().defaultContent(); - expect(await record.getAttribute('class')).toEqual(notSelected); -}); + await counter.click(); -it('selects and deselects trooper by clicking on its record', async () => { - const record = await driver.findElement(By.css('.soldier-record')); - const objectEl = await driver.findElement(By.css('object')); - const selector = '.counter[data-allegiance="attacker"][data-number="1"]'; + await page.expectSelected(counter); + await driver.switchTo().defaultContent(); + await page.expectSelected(await record); + }); - expect(await record.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().frame(objectEl); - const svg = await driver.findElement(By.css('svg')); - const counter = await driver.findElement(By.css(selector), svg); - expect(await counter.getAttribute('class')).toEqual(notSelected); + it('is selected by clicking on its record', async () => { + await driver.get(url('/')); + await page.waitUntilMapLoaded(driver); - await driver.switchTo().defaultContent(); - await record.click(); + const record = page.findTroopRecordEl(driver); + const mapResource = page.findMapResourceEl(driver); - expect(await record.getAttribute('class')).toEqual(selected); - await driver.switchTo().frame(objectEl); - expect(await counter.getAttribute('class')).toEqual(selected); + await page.expectNotSelected(await record); + const counter = await page.findElInFrame(await mapResource, By.css(page.troopCounterSelector())); + await page.expectNotSelected(counter); - await driver.switchTo().defaultContent(); - await record.click(); + await driver.switchTo().defaultContent(); + await record.click(); - expect(await record.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().frame(objectEl); - expect(await counter.getAttribute('class')).toEqual(notSelected); + await page.expectSelected(await record); + await driver.switchTo().frame(await mapResource); + await page.expectSelected(counter); + }); }); -it('selects a trooper by clicking on its counter and deselects it by clicking on its record', async () => { - const record = await driver.findElement(By.css('.soldier-record')); - const objectEl = await driver.findElement(By.css('object')); - const selector = '.counter[data-allegiance="attacker"][data-number="1"]'; - - expect(await record.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().frame(objectEl); - const svg = await driver.findElement(By.css('svg')); - const counter = await driver.findElement(By.css(selector), svg); - expect(await counter.getAttribute('class')).toEqual(notSelected); +describe('a selected trooper', () => { + beforeEach(async () => { + await mockResponse(driver, `/assets/images/${scenario}`, fixture, (document) => { + placeCounter(document, selectCounter(createTroopCounter()), { x: 1, y: 1 }); + return `<?xml version="1.0" standalone="yes"?>\n` + document.querySelector('svg').outerHTML; + }); + }); - await counter.click(); + it('is deselected by clicking on its counter', async () => { + await driver.get(url('/')); + await page.waitUntilMapLoaded(driver); - expect(await counter.getAttribute('class')).toEqual(selected); - await driver.switchTo().defaultContent(); - expect(await record.getAttribute('class')).toEqual(selected); + const record = page.findTroopRecordEl(driver); + const mapResource = page.findMapResourceEl(driver); - await record.click(); + await page.expectSelected(await record); + const counter = await page.findElInFrame(await mapResource, By.css(page.troopCounterSelector())); + await page.expectSelected(counter); - expect(await record.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().frame(objectEl); - expect(await counter.getAttribute('class')).toEqual(notSelected); -}); + await counter.click(); -it('selects a trooper by clicking on its record and deselects it by clicking on its counter', async () => { - const record = await driver.findElement(By.css('.soldier-record')); - const objectEl = await driver.findElement(By.css('object')); - const selector = '.counter[data-allegiance="attacker"][data-number="1"]'; + await page.expectNotSelected(counter); + await driver.switchTo().defaultContent(); + await page.expectNotSelected(await record); + }); - expect(await record.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().frame(objectEl); - const svg = await driver.findElement(By.css('svg')); - const counter = await driver.findElement(By.css(selector), svg); - expect(await counter.getAttribute('class')).toEqual(notSelected); + it('is deselected by clicking on its record', async () => { + await driver.get(url('/')); + await page.waitUntilMapLoaded(driver); - await driver.switchTo().defaultContent(); - await record.click(); + const record = page.findTroopRecordEl(driver); + const mapResource = page.findMapResourceEl(driver); - expect(await record.getAttribute('class')).toEqual(selected); - await driver.switchTo().frame(objectEl); - expect(await counter.getAttribute('class')).toEqual(selected); + await page.expectSelected(await record); + const counter = await page.findElInFrame(await mapResource, By.css(page.troopCounterSelector())); + await page.expectSelected(counter); - await counter.click(); + await driver.switchTo().defaultContent(); + await record.click(); - expect(await counter.getAttribute('class')).toEqual(notSelected); - await driver.switchTo().defaultContent(); - expect(await record.getAttribute('class')).toEqual(notSelected); + await page.expectNotSelected(await record); + await driver.switchTo().frame(await mapResource); + await page.expectNotSelected(counter); + }); }); afterEach(async () => { |