Playwright start
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
class BasePage {
|
||||
constructor(website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
get url() {
|
||||
throw new Error('Subclasses must implement url');
|
||||
}
|
||||
|
||||
async getLinks() {
|
||||
const content = this.website.find('content');
|
||||
const links = content.locator('a');
|
||||
return await links.evaluateAll(els => els.map(el => el.getAttribute('href')).filter(Boolean));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BasePage;
|
||||
@@ -0,0 +1,52 @@
|
||||
class ArmyComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
armyView() {
|
||||
return this.page.locator('.armyView');
|
||||
}
|
||||
|
||||
displayValue(label) {
|
||||
return this.page.locator('.displayContainer').filter({ hasText: label }).locator('.displayContent');
|
||||
}
|
||||
|
||||
armyCards() {
|
||||
return this.armyView().locator('.armyCard');
|
||||
}
|
||||
|
||||
async getArmyCompletedAt() {
|
||||
return await this.displayValue('Army Completed At').textContent();
|
||||
}
|
||||
|
||||
async getArmyAttackingAt() {
|
||||
return await this.displayValue('Army Attacking At').textContent();
|
||||
}
|
||||
|
||||
async getArmyUnitNames() {
|
||||
const cards = await this.armyCards().all();
|
||||
const names = [];
|
||||
for (const card of cards) {
|
||||
const text = await card.innerText();
|
||||
const match = text.match(/\d+x\s*(.+)/);
|
||||
names.push(match ? match[1].trim() : text.trim());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
async getArmyUnitCounts() {
|
||||
const cards = await this.armyCards().all();
|
||||
const counts = [];
|
||||
for (const card of cards) {
|
||||
const countEl = card.locator('.armyCount');
|
||||
const nameEl = card.locator('div').last();
|
||||
const count = await countEl.textContent();
|
||||
const name = await nameEl.textContent();
|
||||
const num = count ? parseInt(count.replace('x', ''), 10) : 0;
|
||||
counts.push({ name: (name || '').trim(), count: num });
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ArmyComponent;
|
||||
@@ -0,0 +1,47 @@
|
||||
class BankComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
bankContainer() {
|
||||
return this.page.locator('.bankContainer');
|
||||
}
|
||||
|
||||
displayValue(label) {
|
||||
return this.bankContainer().locator('.displayContainer').filter({ hasText: label }).locator('.displayContent');
|
||||
}
|
||||
|
||||
async getTime() {
|
||||
return await this.displayValue('Time').textContent();
|
||||
}
|
||||
|
||||
async getAlloy() {
|
||||
return await this.displayValue('Alloy').textContent();
|
||||
}
|
||||
|
||||
async getEther() {
|
||||
return await this.displayValue('Ether').textContent();
|
||||
}
|
||||
|
||||
async getPyre() {
|
||||
return await this.displayValue('Pyre').textContent();
|
||||
}
|
||||
|
||||
async getSupply() {
|
||||
return await this.displayValue('Supply').textContent();
|
||||
}
|
||||
|
||||
async getWorkerCount() {
|
||||
return await this.bankContainer().locator('.workerText').locator('.displayContent').nth(0).textContent();
|
||||
}
|
||||
|
||||
async getBusyWorkerCount() {
|
||||
return await this.bankContainer().locator('.workerText').locator('.displayContent').nth(1).textContent();
|
||||
}
|
||||
|
||||
async getCreatingWorkerCount() {
|
||||
return await this.bankContainer().locator('.workerText').locator('.displayContent').nth(2).textContent();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BankComponent;
|
||||
@@ -0,0 +1,35 @@
|
||||
class BuildChartComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
chartsContainer() {
|
||||
return this.page.locator('.chartsContainer');
|
||||
}
|
||||
|
||||
displayValue(label) {
|
||||
return this.page.locator('.displayContainer').filter({ hasText: label }).locator('.displayContent');
|
||||
}
|
||||
|
||||
async getHighestAlloy() {
|
||||
return await this.displayValue('Highest Alloy').textContent();
|
||||
}
|
||||
|
||||
async getHighestEther() {
|
||||
return await this.displayValue('Highest Ether').textContent();
|
||||
}
|
||||
|
||||
async getHighestPyre() {
|
||||
return await this.displayValue('Highest Pyre').textContent();
|
||||
}
|
||||
|
||||
async getHighestArmy() {
|
||||
return await this.displayValue('Highest Army').textContent();
|
||||
}
|
||||
|
||||
async getChartCount() {
|
||||
return await this.chartsContainer().locator('> div').count();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildChartComponent;
|
||||
@@ -0,0 +1,15 @@
|
||||
class BuildOrderComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
jsonTextarea() {
|
||||
return this.page.locator('textarea');
|
||||
}
|
||||
|
||||
async getJsonData() {
|
||||
return await this.jsonTextarea().inputValue();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildOrderComponent;
|
||||
@@ -0,0 +1,33 @@
|
||||
class EntityClickViewComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
entityClickView() {
|
||||
return this.page.locator('.entityClickView');
|
||||
}
|
||||
|
||||
async getEntityName() {
|
||||
const el = this.entityClickView().locator('#entityName');
|
||||
if ((await el.count()) === 0) return null;
|
||||
return (await el.textContent()) || '';
|
||||
}
|
||||
|
||||
async getEntityHealth() {
|
||||
const healthText = this.entityClickView().locator('div').filter({ hasText: /Health/i }).first();
|
||||
if ((await healthText.count()) === 0) return null;
|
||||
const text = (await healthText.textContent()) || '';
|
||||
const match = text.match(/(\d+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
async clickDetailedView() {
|
||||
await this.entityClickView().locator('button').filter({ hasText: 'Detailed' }).click();
|
||||
}
|
||||
|
||||
async clickPlainView() {
|
||||
await this.entityClickView().locator('button').filter({ hasText: 'Plain' }).click();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntityClickViewComponent;
|
||||
@@ -0,0 +1,35 @@
|
||||
class FilterComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
factionSelect() {
|
||||
return this.page.locator('select').filter({ has: this.page.locator('option:has-text("Aru"), option:has-text("Q\'Rath")') });
|
||||
}
|
||||
|
||||
immortalSelect() {
|
||||
return this.page.locator('select').filter({ has: this.page.locator('option:has-text("Orzum"), option:has-text("Ajari"), option:has-text("Atzlan"), option:has-text("Mala"), option:has-text("Xol")') });
|
||||
}
|
||||
|
||||
async selectFaction(faction) {
|
||||
await this.factionSelect().selectOption(faction);
|
||||
}
|
||||
|
||||
async selectImmortal(immortal) {
|
||||
await this.immortalSelect().selectOption(immortal);
|
||||
}
|
||||
|
||||
async getSelectedFaction() {
|
||||
return await this.factionSelect().inputValue();
|
||||
}
|
||||
|
||||
async getSelectedImmortal() {
|
||||
return await this.immortalSelect().inputValue();
|
||||
}
|
||||
|
||||
async getAvailableImmortals() {
|
||||
return await this.immortalSelect().locator('option').allTextContents();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FilterComponent;
|
||||
@@ -0,0 +1,39 @@
|
||||
class HighlightsComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
highlightsContainer() {
|
||||
return this.page.locator('.highlightsContainer');
|
||||
}
|
||||
|
||||
requestedColumn() {
|
||||
return this.highlightsContainer().locator('div').filter({ hasText: 'Requested' }).locator('+ div');
|
||||
}
|
||||
|
||||
finishedColumn() {
|
||||
return this.highlightsContainer().locator('div').filter({ hasText: 'Finished' }).locator('+ div');
|
||||
}
|
||||
|
||||
async getRequestedItems() {
|
||||
const items = await this.highlightsContainer().locator('div').filter({ hasText: /^\d+\s*\|/ }).all();
|
||||
const result = [];
|
||||
for (const item of items) {
|
||||
const text = (await item.textContent()) || '';
|
||||
result.push(text.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async getFinishedItems() {
|
||||
const items = await this.highlightsContainer().locator('div').filter({ hasText: /^\d+\s*\|/ }).all();
|
||||
const result = [];
|
||||
for (const item of items) {
|
||||
const text = (await item.textContent()) || '';
|
||||
result.push(text.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HighlightsComponent;
|
||||
@@ -0,0 +1,50 @@
|
||||
class HotkeyViewerComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
keyContainer() {
|
||||
return this.page.locator('.keyContainer');
|
||||
}
|
||||
|
||||
async _findKeyButton(keyLabel) {
|
||||
const upper = keyLabel.toUpperCase();
|
||||
const buttons = this.keyContainer().locator('> div > div');
|
||||
const count = await buttons.count();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const btn = buttons.nth(i);
|
||||
const text = (await btn.textContent()) || '';
|
||||
if (text.trim().toUpperCase().startsWith(upper)) return btn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async clickKey(keyText) {
|
||||
const btn = await this._findKeyButton(keyText);
|
||||
if (!btn) throw new Error(`Key "${keyText}" not found`);
|
||||
await btn.click({ force: true });
|
||||
}
|
||||
|
||||
async getFirstEntityName(keyText) {
|
||||
const btn = await this._findKeyButton(keyText);
|
||||
if (!btn) return null;
|
||||
const entities = btn.locator('> div');
|
||||
if ((await entities.count()) === 0) return null;
|
||||
return (await entities.first().textContent()) || '';
|
||||
}
|
||||
|
||||
async getEntityNamesOnKey(keyText) {
|
||||
const btn = await this._findKeyButton(keyText);
|
||||
if (!btn) return [];
|
||||
const entities = btn.locator('> div');
|
||||
const count = await entities.count();
|
||||
const names = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const text = (await entities.nth(i).textContent()) || '';
|
||||
names.push(text.trim());
|
||||
}
|
||||
return names.filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HotkeyViewerComponent;
|
||||
@@ -0,0 +1,68 @@
|
||||
class OptionsComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
buildingInputDelayInput() {
|
||||
return this.formNumberInput('Building Input Delay');
|
||||
}
|
||||
|
||||
waitTimeInput() {
|
||||
return this.formNumberInput('Wait Time');
|
||||
}
|
||||
|
||||
waitToInput() {
|
||||
return this.formNumberInput('Wait To');
|
||||
}
|
||||
|
||||
addWaitButton() {
|
||||
return this.buttonWithLabel('Add Wait').first();
|
||||
}
|
||||
|
||||
addWaitToButton() {
|
||||
return this.buttonWithLabel('Add Wait').last();
|
||||
}
|
||||
|
||||
formNumberInput(label) {
|
||||
return this.page.locator(`.formNumberContainer`).filter({ hasText: label }).locator('input[type="number"]');
|
||||
}
|
||||
|
||||
buttonWithLabel(label) {
|
||||
return this.page.locator('button').filter({ hasText: label });
|
||||
}
|
||||
|
||||
async setBuildingInputDelay(value) {
|
||||
await this.buildingInputDelayInput().fill(String(value));
|
||||
await this.buildingInputDelayInput().press('Enter');
|
||||
}
|
||||
|
||||
async setWaitTime(value) {
|
||||
await this.waitTimeInput().fill(String(value));
|
||||
}
|
||||
|
||||
async setWaitTo(value) {
|
||||
await this.waitToInput().fill(String(value));
|
||||
}
|
||||
|
||||
async clickAddWait() {
|
||||
await this.addWaitButton().click();
|
||||
}
|
||||
|
||||
async clickAddWaitTo() {
|
||||
await this.addWaitToButton().click();
|
||||
}
|
||||
|
||||
async getBuildingInputDelay() {
|
||||
return await this.buildingInputDelayInput().inputValue();
|
||||
}
|
||||
|
||||
async getWaitTime() {
|
||||
return await this.waitTimeInput().inputValue();
|
||||
}
|
||||
|
||||
async getWaitTo() {
|
||||
return await this.waitToInput().inputValue();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OptionsComponent;
|
||||
@@ -0,0 +1,16 @@
|
||||
class TimelineComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
container() {
|
||||
return this.page.locator('.calculatorGrid > div').filter({ hasText: 'Timeline highlights' });
|
||||
}
|
||||
|
||||
async containsEntity(name) {
|
||||
const text = (await this.container().textContent()) || '';
|
||||
return text.includes(name);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimelineComponent;
|
||||
@@ -0,0 +1,37 @@
|
||||
class TimingComponent {
|
||||
constructor(website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
attackTimeInput() {
|
||||
return this.formNumberInput('Attack Time');
|
||||
}
|
||||
|
||||
travelTimeInput() {
|
||||
return this.formNumberInput('Travel Time');
|
||||
}
|
||||
|
||||
formNumberInput(label) {
|
||||
return this.website.locator(`.formNumberContainer`).filter({ hasText: label }).locator('input[type="number"]');
|
||||
}
|
||||
|
||||
async setAttackTime(value) {
|
||||
await this.attackTimeInput().fill(String(value));
|
||||
await this.attackTimeInput().press('Enter');
|
||||
}
|
||||
|
||||
async setTravelTime(value) {
|
||||
await this.travelTimeInput().fill(String(value));
|
||||
await this.travelTimeInput().press('Enter');
|
||||
}
|
||||
|
||||
async getAttackTime() {
|
||||
return await this.attackTimeInput().inputValue();
|
||||
}
|
||||
|
||||
async getTravelTime() {
|
||||
return await this.travelTimeInput().inputValue();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimingComponent;
|
||||
@@ -0,0 +1,56 @@
|
||||
const TimingComponent = require('./buildCalculator/timingComponent');
|
||||
const FilterComponent = require('./buildCalculator/filterComponent');
|
||||
const OptionsComponent = require('./buildCalculator/optionsComponent');
|
||||
const BankComponent = require('./buildCalculator/bankComponent');
|
||||
const ArmyComponent = require('./buildCalculator/armyComponent');
|
||||
const HighlightsComponent = require('./buildCalculator/highlightsComponent');
|
||||
const BuildOrderComponent = require('./buildCalculator/buildOrderComponent');
|
||||
const TimelineComponent = require('./buildCalculator/timelineComponent');
|
||||
const HotkeyViewerComponent = require('./buildCalculator/hotkeyViewerComponent');
|
||||
const EntityClickViewComponent = require('./buildCalculator/entityClickViewComponent');
|
||||
const BuildChartComponent = require('./buildCalculator/buildChartComponent');
|
||||
const ToastComponent = require('../shared/toastComponent');
|
||||
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
|
||||
class BuildCalculatorPage extends BasePage {
|
||||
constructor(website) {
|
||||
super(website);
|
||||
this.timing = new TimingComponent(website);
|
||||
this.filter = new FilterComponent(website);
|
||||
this.options = new OptionsComponent(website);
|
||||
this.bank = new BankComponent(website);
|
||||
this.army = new ArmyComponent(website);
|
||||
this.highlights = new HighlightsComponent(website);
|
||||
this.buildOrder = new BuildOrderComponent(website);
|
||||
this.timeline = new TimelineComponent(website);
|
||||
this.hotkeys = new HotkeyViewerComponent(website);
|
||||
this.entityView = new EntityClickViewComponent(website);
|
||||
this.chart = new BuildChartComponent(website);
|
||||
this.toast = new ToastComponent(website);
|
||||
}
|
||||
|
||||
get url() {
|
||||
return 'build-calculator';
|
||||
}
|
||||
|
||||
calculatorGrid() {
|
||||
return this.website.locator('.calculatorGrid');
|
||||
}
|
||||
|
||||
clearBuildOrderButton() {
|
||||
return this.website.locator('button').filter({ hasText: 'Clear Build Order' });
|
||||
}
|
||||
|
||||
async clickClearBuildOrder() {
|
||||
await this.clearBuildOrderButton().click();
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.website.goto(this.url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildCalculatorPage;
|
||||
@@ -0,0 +1,27 @@
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
class DatabasePage extends BasePage {
|
||||
get url() { return 'database'; }
|
||||
|
||||
async filterName(name) {
|
||||
await this.website.enterInput(this.website.findAll('filterName').first(), name);
|
||||
return this;
|
||||
}
|
||||
|
||||
async getEntityName(entityType, entityName) {
|
||||
return await this.website
|
||||
.findWithParent('entityName', `${entityType.toLowerCase()}-${entityName.toLowerCase()}`)
|
||||
.innerText();
|
||||
}
|
||||
|
||||
async getEntityNameByIndex(index) {
|
||||
return await this.website.findAll('entityName').nth(index).innerText();
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.website.goto(this.url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DatabasePage;
|
||||
@@ -0,0 +1,28 @@
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
class DatabaseSinglePage extends BasePage {
|
||||
get url() { return 'database'; }
|
||||
|
||||
async getEntityName() {
|
||||
return await this.website.find('entityName').innerText();
|
||||
}
|
||||
|
||||
async getEntityHealth() {
|
||||
return await this.website.find('entityHealth').innerText();
|
||||
}
|
||||
|
||||
async getInvalidSearch() {
|
||||
return await this.website.find('invalidSearch').innerText();
|
||||
}
|
||||
|
||||
async getValidSearch() {
|
||||
return await this.website.find('validSearch').innerText();
|
||||
}
|
||||
|
||||
async goto(searchText) {
|
||||
await this.website.goto(`${this.url}/${searchText}`);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DatabaseSinglePage;
|
||||
@@ -0,0 +1,68 @@
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
class HarassCalculatorPage extends BasePage {
|
||||
get url() { return 'harass-calculator'; }
|
||||
|
||||
async setWorkersLostToHarass(number) {
|
||||
await this.website.enterInput(this.website.find('numberOfWorkersLostToHarass'), number);
|
||||
return this;
|
||||
}
|
||||
|
||||
async setNumberOfTownHallsExisting(number) {
|
||||
await this.website.enterInput(this.website.find('numberOfTownHallsExisting'), number);
|
||||
return this;
|
||||
}
|
||||
|
||||
async setTownHallTravelTime(forTownHall, number) {
|
||||
const inputs = this.website.findChildren('numberOfTownHallTravelTimes', 'input');
|
||||
await this.website.enterInput(inputs.nth(forTownHall), number);
|
||||
return this;
|
||||
}
|
||||
|
||||
async getTotalAlloyHarassment() {
|
||||
return await this.website.findInt('totalAlloyHarassment');
|
||||
}
|
||||
|
||||
async getWorkerReplacementCost() {
|
||||
return await this.website.findInt('workerReplacementCost');
|
||||
}
|
||||
|
||||
async getDelayedMiningCost() {
|
||||
return await this.website.findInt('delayedMiningCost');
|
||||
}
|
||||
|
||||
async getAverageTravelTime() {
|
||||
return await this.website.findInt('getAverageTravelTime');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLoss() {
|
||||
return await this.website.findInt('exampleTotalAlloyLoss');
|
||||
}
|
||||
|
||||
async getExampleWorkerCost() {
|
||||
return await this.website.findInt('exampleWorkerCost');
|
||||
}
|
||||
|
||||
async getExampleMiningTimeCost() {
|
||||
return await this.website.findInt('exampleMiningTimeCost');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLossAccurate() {
|
||||
return await this.website.findInt('exampleTotalAlloyLossAccurate');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLossDifference() {
|
||||
return await this.website.findInt('exampleTotalAlloyLossDifference');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLossAccurateDifference() {
|
||||
return await this.website.findInt('exampleTotalAlloyLossAccurateDifference');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.website.goto(this.url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HarassCalculatorPage;
|
||||
Reference in New Issue
Block a user