Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions e2e-tests/fixtures/Constraints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,14 @@ export class Constraints {
await this.page.waitForURL(`${baseURL}/constraints`);
}

async deleteConstraint() {
async deleteConstraint(name: string) {
await this.goto();
await this.filterTable(this.constraintName);
this.tableRow = await this.table.getByRole('row', { name });
this.tableRowDeleteButton = await this.tableRow
.getByRole('gridcell')
.getByRole('button', { name: 'Delete Constraint' });

await this.filterTable(name);
await expect(this.tableRow).toBeVisible();

await this.tableRow.hover();
Expand Down
18 changes: 13 additions & 5 deletions e2e-tests/fixtures/Plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class Plan {
constraintManageButton: Locator;
constraintModalFilter: Locator;
constraintNewButton: Locator;
constraintStatusSelector: (status: string) => string;
externalSourceManageButton: Locator;
gridMenu: Locator;
gridMenuButton: Locator;
Expand Down Expand Up @@ -96,6 +97,8 @@ export class Plan {
public planName = plans.planName,
) {
this.constraintListItemSelector = `.constraint-list-item:has-text("${constraints.constraintName}")`;
this.constraintStatusSelector = (status: string) =>
`.nav-button:has-text("Constraints") .status-badge[aria-label=${status}]`;
this.schedulingConditionListItemSelector = (conditionName: string) =>
`.scheduling-condition:has-text("${conditionName}")`;
this.schedulingGoalListItemSelector = (goalName: string) => `.scheduling-goal:has-text("${goalName}")`;
Expand Down Expand Up @@ -485,18 +488,18 @@ export class Plan {
await this.waitForSimulationStatus(expectedFinalState);
}

async removeConstraint() {
async removeConstraint(name: string) {
await this.constraintManageButton.click();
await this.constraintModalFilter.fill(this.constraints.constraintName);
const row = this.page.getByRole('row', { name: this.constraints.constraintName });
await this.constraintModalFilter.fill(name);
const row = this.page.getByRole('row', { name });
await expect(row).toBeVisible();
// Use click with force for AG Grid checkboxes - check/uncheck fails with Chrome for Testing
const checkbox = row.getByRole('checkbox');
await expect(checkbox).toBeChecked();
await checkbox.click({ force: true });
await expect(checkbox).not.toBeChecked();
await this.page.getByRole('button', { name: 'Update' }).click();
await this.page.locator(this.constraintListItemSelector).waitFor({ state: 'detached' });
await this.page.locator(this.constraintListItemSelector).first().waitFor({ state: 'detached' });
}

async removePlanCollaborator(name: string) {
Expand Down Expand Up @@ -738,7 +741,7 @@ export class Plan {
this.changeMissionModelMigrateButton = this.changeMissionModelModal.getByRole('button', {
name: 'Change Mission Model',
});
this.constraintManageButton = page.locator(`button[name="manage-constraints"]`);
this.constraintManageButton = page.getByRole('button', { name: 'Manage Constraints' }).first();
this.constraintModalFilter = page.locator('.modal').getByPlaceholder('Filter constraints');
this.constraintNewButton = page.locator(`button[name="new-constraint"]`);
this.consoleContainer = page.getByTestId('console');
Expand Down Expand Up @@ -832,6 +835,11 @@ export class Plan {
await expect(this.page.locator(this.activityCheckingStatusSelector(status))).toBeVisible();
}

async waitForConstraintStatus(status: string) {
await expect(this.page.locator(this.constraintStatusSelector(status))).toBeAttached({ timeout: 10000 });
await expect(this.page.locator(this.constraintStatusSelector(status))).toBeVisible();
}

async waitForPlanCollaboratorLoad() {
await expect(this.planCollaboratorInputContainer).toBeVisible({ timeout: 10000 });
await expect(this.planCollaboratorLoadingInput).not.toBeVisible({ timeout: 10000 });
Expand Down
40 changes: 37 additions & 3 deletions e2e-tests/tests/constraints.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import test from '@playwright/test';
import test, { expect } from '@playwright/test';
import { adjectives, animals, colors, uniqueNamesGenerator } from 'unique-names-generator';
import { Status } from '../../src/enums/status.js';
import { PanelNames } from '../fixtures/Plan.js';
import { setupTest, teardownTest, type FullSetupResult } from '../utilities/api.js';

let setup: FullSetupResult;
let originalConstraintName: string;
const newConstraintName: string =
'FAILING_TEST_CONSTRAINT_' + uniqueNamesGenerator({ dictionaries: [adjectives, colors, animals] });
const newConstraintDefinition: string =
"export default function peelFailing(): Constraint { return Real.Resource('/peel').lessThan(-1000); }";

test.beforeAll(async ({ browser }) => {
setup = await setupTest(browser);
Expand All @@ -21,8 +29,34 @@ test.describe.serial('Constraints', () => {
await setup.plan.createConstraint(baseURL);
});

// largely a test of backend functionality, but also tests that constraint results show correctly
test('Evaluate a failing and passing constraint', async ({ baseURL }) => {
originalConstraintName = setup.plan.constraints.constraintName;

// create a new constraint
setup.plan.constraints.constraintName = newConstraintName;
setup.plan.constraints.constraintDefinition = newConstraintDefinition;
await setup.plan.createConstraint(baseURL);

// simulate
await setup.plan.showPanel(PanelNames.SIMULATION, true);
await setup.plan.runSimulation(Status.Complete);

// evaluate constraints
await setup.page.getByRole('navigation').getByText('Constraints').click();
await setup.page.getByText('Check Constraints', { exact: true }).click();

// check results, should say "2 of 2 constraints, 1 of 1 violations"
await setup.plan.showPanel(PanelNames.CONSTRAINTS, true);
await expect(setup.page.getByText('2 of 2 constraints, 1 of 1').first()).toBeVisible({ timeout: 10000 });
});

test('Delete constraint', async () => {
await setup.plan.removeConstraint();
await setup.constraints.deleteConstraint();
await setup.plan.removeConstraint(originalConstraintName);
await setup.plan.removeConstraint(newConstraintName);

await setup.page.pause();
await setup.constraints.deleteConstraint(originalConstraintName);
await setup.constraints.deleteConstraint(newConstraintName);
});
});
61 changes: 60 additions & 1 deletion e2e-tests/tests/plan-expansion.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import test, { expect } from '@playwright/test';
import { adjectives, animals, colors, uniqueNamesGenerator } from 'unique-names-generator';
import { Dictionaries } from '../fixtures/Dictionaries.js';
import { ExpansionRules } from '../fixtures/ExpansionRules.js';
import { ExpansionSets } from '../fixtures/ExpansionSets.js';
import { Parcels } from '../fixtures/Parcels.js';
import { PanelNames } from '../fixtures/Plan.js';
import { setupTest, teardownTest, type FullSetupResult } from '../utilities/api.js';
Expand All @@ -12,11 +14,15 @@ let setup: FullSetupResult;
let dictionaryName: string;
let dictionaries: Dictionaries;
let parcels: Parcels;
let expansionRules: ExpansionRules;
let expansionSets: ExpansionSets;

test.beforeAll(async ({ baseURL, browser }) => {
setup = await setupTest(browser);
dictionaries = new Dictionaries(setup.page);
parcels = new Parcels(setup.page);
expansionRules = new ExpansionRules(setup.page, parcels, setup.models);
expansionSets = new ExpansionSets(setup.page, parcels, setup.models, expansionRules);

await dictionaries.goto();
await dictionaries.createCommandDictionary();
Expand All @@ -26,7 +32,6 @@ test.beforeAll(async ({ baseURL, browser }) => {
});

test.afterAll(async () => {
await parcels.goto();
await teardownTest(setup);
});

Expand Down Expand Up @@ -58,4 +63,58 @@ test.describe.serial('Plan Expansion', () => {
await setup.plan.showPanel(PanelNames.EXPANSION);
await setup.plan.applySequenceFilter(sequenceFilterName, setup.plans.planId);
});

test('Planners warned if constraints have issues', async ({ baseURL }) => {
await expansionRules.goto();
await expansionRules.createExpansionRule(baseURL);
await expansionSets.goto();
await expansionSets.createExpansionSet(baseURL);
const expansionSetId = await expansionSets.page
.getByRole('tabpanel')
.filter({ hasText: 'Expansion Sets' })
.getByRole('treegrid')
.getByRole('row', { name: expansionSets.expansionSetName })
.getByRole('gridcell')
.first()
.textContent();

await setup.plan.goto();

setup.plan.constraints.constraintDefinition =
"export default function peelFailing(): Constraint { return Real.Resource('/peel').lessThan(-1000); }";
await setup.plan.showConstraintsLayout();
await setup.plan.createConstraint(baseURL);

// running a simulation on the plan is required before being able to check constraints
await setup.plan.showPanel(PanelNames.SIMULATION, true);
// run the simulation if it already hasn't been
if (await setup.plan.simulateButton.isEnabled()) {
await setup.plan.runSimulation();
}

// evaluate constraints
await setup.plan.navButtonConstraints.click();
await setup.plan.navButtonConstraintsMenu.getByText('Check Constraints', { exact: true }).click();

// expand
await setup.plan.showPanel(PanelNames.EXPANSION);

await setup.page
.locator('select[name="expansionSetId"]')
.selectOption({ label: `${expansionSets.expansionSetName} (${expansionSetId})` });

const sequenceName = `${sequenceFilterName} Sequence (Plan ${setup.planId})`;
await setup.page.getByText(sequenceName, { exact: true }).hover();

const expansionButton = setup.page.getByRole('button', { name: `Expand '${sequenceName}'` });
await expansionButton.waitFor({ state: 'visible' });
await expansionButton.click();

// check warning
await expect(setup.page.getByText('Violating Constraints')).toBeVisible();
await expect(setup.page.getByText('This constraint is currently')).toBeVisible();
await expect(setup.page.getByText('(1 violation)')).toBeVisible();

await setup.page.getByRole('button', { name: 'Expand Anyways' }).click();
});
});
Loading