import { AppBase } from '../libappbase/appbase';
|
import {
|
ActionBarPageData,
|
ActionBarPageDevelop,
|
ActionBarPageHome,
|
ActionBarPageInventory,
|
ActionBarPageScenario,
|
ActionBarPageScenarioAnalysis,
|
ActionBarPagePlan,
|
ActionBarPageScenarioSelection,
|
ActionBarPageSales,
|
ActionBarPageScenarioComparison,
|
ActionBarPageWorkflow,
|
} from './actionbarpages';
|
import { DialogSelectDemoDataset } from './dialogs/dialog.demodataset';
|
import { DialogStrategy } from './dialogs/dialog.strategy';
|
import { FormKPI, GaugeIDValueMap, ListKPISelectionContextMenuItem } from './forms/form.kpi';
|
import { FormNavigationPanel } from './forms/navigationpanel/form.navigationpanel';
|
import {
|
ViewAccount,
|
ViewEntity,
|
ViewExternalSupplies,
|
ViewInventoryCosts,
|
ViewOperationCost,
|
ViewStockingCost,
|
ViewUnitCost,
|
ViewOptimizerStrategies,
|
ViewCurrency,
|
ViewPeriod,
|
ViewScenarioAnalysisCost,
|
ViewScenario,
|
ViewSupplyPlanning,
|
ViewProduct,
|
ViewGeneralSettings,
|
ViewCampaignsAndTransitions,
|
ViewCampaigns,
|
ViewStockCapacity,
|
ViewSalesSegments,
|
ViewUnitsOfMeasure,
|
ViewScenarioAnalysisOverview,
|
ViewRecipes,
|
ViewCapacitiesTime,
|
ViewCapacitiesQuantity,
|
ViewCapacitiesStockingPoints,
|
ViewScenarioComparisonCost,
|
ViewForecasts,
|
ViewCustomerOrders,
|
ViewFeedback,
|
ViewProductionPlan,
|
ViewSupplyCost,
|
ViewCalendar,
|
ViewTripPlan,
|
ViewPurchasingPlan,
|
ViewTransportationCost,
|
ViewBlendingPlan,
|
ViewRouting,
|
} from './views';
|
import { DataKPIName, DataKPIPercentage } from '../libmp/data/data.kpi';
|
import { by, ElementFinder } from '../e2elib/node_modules/protractor/built';
|
import { ListScenarioContextMenuItem, ScenarioManager } from './forms/form.scenario';
|
import { ViewBase } from '../libappbase/viewbase';
|
import { UIWaitSOP } from '../libappsop/objectsop';
|
import { DropDownListSOP } from '../libappsop/dropdownlistsop';
|
import { ButtonSOP } from '../libappsop/buttonsop';
|
import { ViewScenarioAnalysisUnitCapacity } from './views/view.scenarioanalysis.unitcapacity';
|
import { ViewScenarioAnalysisBottleneckResources } from './views/view.scenarioanalysis.bottleneckresources';
|
|
export class AppMP extends AppBase {
|
private static _appMP: AppMP;
|
|
// Action bar page
|
public abpDevelop = new ActionBarPageDevelop();
|
public abpInventory = new ActionBarPageInventory();
|
public abpScenario = new ActionBarPageScenario();
|
public abpPlan = new ActionBarPagePlan();
|
public abpHome = new ActionBarPageHome();
|
public abpScenarioAnalysis = new ActionBarPageScenarioAnalysis();
|
public abpScenarioSelection = new ActionBarPageScenarioSelection();
|
public abpSales = new ActionBarPageSales();
|
public abpData = new ActionBarPageData();
|
public abpScenarioComparison = new ActionBarPageScenarioComparison();
|
public abpWorkflow = new ActionBarPageWorkflow();
|
|
// Views
|
public viewScenario = new ViewScenario();
|
public viewCustomerOrders = new ViewCustomerOrders();
|
public viewCurrency = new ViewCurrency();
|
public viewExternalSupplies = new ViewExternalSupplies();
|
public viewOptimizerStrategies = new ViewOptimizerStrategies();
|
public viewUnitCost = new ViewUnitCost();
|
public viewSupplyPlanning = new ViewSupplyPlanning();
|
public viewInventoryCosts = new ViewInventoryCosts();
|
public viewAccount = new ViewAccount();
|
public viewEntity = new ViewEntity();
|
public viewForecasts = new ViewForecasts();
|
public viewOperationCost = new ViewOperationCost();
|
public viewStockingCost = new ViewStockingCost();
|
public viewSupplyCost = new ViewSupplyCost();
|
public viewScenarioAnalysisOverview = new ViewScenarioAnalysisOverview();
|
public viewScenarioAnalysisCost = new ViewScenarioAnalysisCost();
|
public viewPeriod = new ViewPeriod();
|
public viewProduct = new ViewProduct();
|
public viewRecipes = new ViewRecipes();
|
public viewGeneralSettings = new ViewGeneralSettings();
|
public viewCampaignsAndTransitions = new ViewCampaignsAndTransitions();
|
public viewCampaign = new ViewCampaigns();
|
public viewFeedback = new ViewFeedback();
|
public viewStockCapacity = new ViewStockCapacity();
|
public viewSalesSegments = new ViewSalesSegments();
|
public viewUnitsOfMeasure = new ViewUnitsOfMeasure();
|
public viewCapacitiesTime = new ViewCapacitiesTime();
|
public viewScenarioAnalysisUnitCapacity = new ViewScenarioAnalysisUnitCapacity();
|
public viewScenarioAnalysisBottleneckResources = new ViewScenarioAnalysisBottleneckResources();
|
public viewCapacitiesQuantity = new ViewCapacitiesQuantity();
|
public viewCapacitiesStockingPoints = new ViewCapacitiesStockingPoints();
|
public viewScenarioComparisonCost = new ViewScenarioComparisonCost();
|
public viewCalendar = new ViewCalendar();
|
public viewProductionPlan = new ViewProductionPlan();
|
public viewTripPlan = new ViewTripPlan();
|
public viewPurchasingPlan = new ViewPurchasingPlan();
|
public viewTransportationCost = new ViewTransportationCost();
|
public viewBlendingPlan = new ViewBlendingPlan();
|
public viewRouting = new ViewRouting();
|
|
// Forms
|
public frmKPI = new FormKPI();
|
public formNavigation = new FormNavigationPanel();
|
|
// Dialogs
|
public dlgDemoDataset = new DialogSelectDemoDataset();
|
public dlgStrategy = new DialogStrategy();
|
|
// UI elements at fixed action bar (top right)
|
public btnCreateAssumption = new ButtonSOP('ButtonCreateAssumption');
|
public btnUndo = new ButtonSOP('buttonUndo');
|
public ddlActiveScenario = new DropDownListSOP('DropDownListScenario'); // Shows current selected/active scenario
|
|
// Get singleton instance of AppMP
|
public static getInstance(): AppMP {
|
if (AppMP._appMP === undefined) {
|
AppMP._appMP = new AppMP();
|
}
|
|
return AppMP._appMP;
|
}
|
|
/**
|
* Copied from e2elib qinputtype as the logic is bugged (not able retrieve action link text)
|
* Retrieve action link text of input component such as editfield, etc
|
* Remove once e2elib fixes the bug
|
*
|
* @param element ElementFinder of input component to retrieve action link text
|
* @returns Action link text
|
*/
|
public static async getActionLinkText(element: ElementFinder): Promise<string> {
|
const elem = element.element(by.css('.qActionLink'));
|
|
return elem.isPresent().then((ispresent: boolean) => {
|
if (ispresent) {
|
// Original code from e2elib which is bugged (as actionlinktext is always empty, the actual text is contained within child button html node)
|
// return element.getAttribute('actionlinktext');
|
return elem.getText();
|
} else {
|
return '';
|
}
|
});
|
}
|
|
/**
|
* Returns the step to create demo data for spec file "it" description.
|
*
|
* @param demoType Demo data such as Metal, Food, etc.
|
* @param scenario The scenario within the demo data (e.g Base)
|
* @returns The step informing which UI to use and which demo data to be used
|
*/
|
public static getDemoDataPath(demoType: string, scenario: string): string {
|
return `Open ${AppMP.getInstance().abpDevelop.startDemoPath}, select ${demoType} demo and ${scenario} scenario`;
|
}
|
|
// To avoid initialization externally
|
private constructor() {
|
super();
|
}
|
|
/**
|
* Create demo dataset in MP
|
*
|
* @param demo type of demo, types could be Food or Metals
|
* @param scenario type of scenario
|
* @param needWaitOptimizerComplete Default true to wait for optimizer run completion by checking fulfillment target KPI = 100%
|
* @example await createDemoDataset('Food', 'Base')
|
*/
|
public async createDemoDataset(demo: string, scenario: string, needWaitOptimizerComplete: boolean = true): Promise<void> {
|
// wait until abp develop is visible
|
await this.abpDevelop.isVisible();
|
// Open Develop action bar page
|
await this.abpDevelop.click();
|
// Click on the Start Demo button
|
await this.abpDevelop.btnStartDemo.click();
|
// Wait until Start Demo Dialog is present
|
await this.dlgDemoDataset.waitUntilPresent();
|
|
await this.dlgDemoDataset.selectDemoDataset(demo, scenario);
|
|
if (needWaitOptimizerComplete) {
|
// Wait until the optimizer run finished by verifying KPI Fulfillment Target = 100.0%
|
const gaugeValueMap: GaugeIDValueMap[] = [{ gaugeName: DataKPIName.FulfillmentTarget, value: DataKPIPercentage._100_0 }];
|
await this.waitForKPICompletion(gaugeValueMap);
|
}
|
}
|
|
/**
|
* Use in each spec's afterall() to cleanup demo datasets and logout.
|
*/
|
public async cleanupAndLogout(): Promise<void> {
|
const excludeDeleteScenarioWithPrefix = 'ADSO-'; // Not to delete scenario with this prefix, reused for few tests
|
|
// Delete all datasets
|
await this.viewScenario.switchTo();
|
await this.viewScenario.formScenario.lstScenario.deleteAllScenarios(ScenarioManager.AllScenarios, excludeDeleteScenarioWithPrefix);
|
await this.viewScenario.formScenario.lstScenario.deleteAllScenarios(ScenarioManager.RecycleBin);
|
await this.logout();
|
}
|
|
/**
|
* Open Scenario view and select scenario as active.
|
*
|
* @param scenarioName Scenario name to set as active.
|
*/
|
public async selectScenario(scenarioName: string): Promise<void> {
|
await this.viewScenario.switchTo();
|
await this.viewScenario.formScenario.lstScenario.expandFolderAndSelectMenu(scenarioName, ListScenarioContextMenuItem.Select);
|
}
|
|
/**
|
* Quick way to select active scenario via top right dropdown which is accesible in all views.
|
* Caveat is the scenario name must be unique to use this method.
|
*
|
* @param scenarioName Scenario name to select.
|
*/
|
public async selectScenarioViaDropdown(scenarioName: string): Promise<void> {
|
await this.ddlActiveScenario.selectItemSOP(scenarioName);
|
await this.verifyActiveScenario(scenarioName);
|
}
|
|
/**
|
* Verify if active scenario dropdown (top right) is showing the correct scenario.
|
*
|
* @param name Expected scenario name to be active.
|
*/
|
public async verifyActiveScenario(name: string): Promise<void> {
|
expect(await this.ddlActiveScenario.getSelectedString()).toBe(name, `Expected scenario '${name}' to be selected in active scenario dropdown (top right).`);
|
}
|
|
/**
|
* Open view Scenario Analyisis > Costs, and ensure the target KPI(s) reaches the expected value
|
*
|
* @param targetKPI target KPI and its expected value
|
*/
|
public async waitForKPICompletion(targetKPI: GaugeIDValueMap[]): Promise<void> {
|
// Open view Scenario Analysis > Costs
|
await this.viewScenarioAnalysisCost.switchTo();
|
// Open KPI Selection panel
|
await this.frmKPI.openKPISelectionPanel();
|
// Deselect all KPI
|
await this.frmKPI.panelKPISelection.lstKPISelection.deselectAllKPI();
|
|
// Select KPI in KPI selection list
|
for (const map of targetKPI) {
|
const row = await this.frmKPI.panelKPISelection.lstKPISelection.getRow({ Name: map.gaugeName });
|
await this.frmKPI.panelKPISelection.lstKPISelection.selectContextMenu(ListKPISelectionContextMenuItem.Select, row);
|
}
|
// Open KPI dashboard panel
|
await this.frmKPI.openKPIDashboardPanel();
|
|
// Wait until the KPI reaches the expected value
|
for (const map of targetKPI) {
|
await this.frmKPI.panelKPIDashboard.gaugeKPIDashboard.waitForKPI(map.gaugeName, map.value);
|
}
|
// Reset view Scenario Analysis > Costs
|
await this.resetActiveView(this.viewScenarioAnalysisCost);
|
}
|
|
/**
|
* Reset active view via Home action bar page. This much faster than opening view manager and search the list to reset.
|
* Local test faster by ~20 seconds which is significant saving.
|
* If use this (compare to the base Reset in ViewBase), ensure reset before opening new view as reset is always on current active view.
|
*
|
* @param view View to reset which should implement @interface UIWaitSOP
|
*/
|
public async resetActiveView(view: ViewBase): Promise<void> {
|
await this.abpHome.click();
|
await this.abpHome.btnResetActiveView.click();
|
|
const viewImplementWaitSOP = view as unknown as UIWaitSOP; // Need cast to unknown before able check if implement interface UIWaitSOP
|
if (viewImplementWaitSOP) {
|
await viewImplementWaitSOP.waitUILoaded();
|
}
|
}
|
}
|
|
export enum Demo {
|
Food = 'Food',
|
Metals = 'Metals',
|
}
|
|
export enum Scenario {
|
Base = 'Base',
|
FoodBaseWithRecall = 'Base + recall',
|
}
|
|
export enum Timeout {
|
Short = 10000,
|
Medium = 30000,
|
Long = 60000,
|
ButtonState = 1000,
|
}
|
|
/**
|
* Image attribute names common to all lists.
|
*/
|
export enum ImageAttribute {
|
IsEmpty = 'EMPTY', // No image attribute
|
IsDefault = 'FLASH', // Default row in list (used by currency and UoM for now)
|
}
|
|
export enum QuintiqUser {
|
Administrator = 'administrator',
|
QuintiqService = 'svc_qtq.sqc', // Admin user in the pipeline (created by DevOps for all teams)
|
}
|
|
// Step description to re-use in spec file to prevent scriptor re-write each time
|
const stepAppMP = {
|
loginAs: (user: string): string => `Login as "${user}".`,
|
logout: (): string => 'Logout from the application.',
|
selectScenario: (scenarioName: string): string =>
|
`Open view ${AppMP.getInstance().viewScenario.viewPath}. Right click scenario '${scenarioName}' and click menu ${
|
ListScenarioContextMenuItem.Select.Label
|
} to set as active scenario.`,
|
selectScenarioViaDropdown: (scenarioName: string): string => `In fixed action bar (top right), select active scenario = ${scenarioName}.`,
|
};
|
|
export { stepAppMP as StepAppMP };
|