yanweiyuan3
2023-08-09 588bc7829387dfc761cc25f06f77d4c81818bd10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
import { QContextMenu } from '../e2elib/lib/api/pageobjects/qcontextmenu.component';
import { GanttChartNode } from '../e2elib/lib/src/pageobjects/ganttchart/ganttchartnode';
import { GanttChartRow } from '../e2elib/lib/src/pageobjects/ganttchart/ganttchartrow';
import { GanttChartBase } from '../libappbase/ganttchartbase';
import { convertDateToUTC } from '../libappbase/timeutils';
import { getHtmlContent } from '../libappbase/utils';
import { ColorSOP } from './colorsop';
import { ContextMenuItemSOP, ContextMenuSOP } from './contextmenusop';
 
export class GanttChartSOP extends GanttChartBase {
  private readonly _contextMenu: QContextMenu;
  private readonly _cmMenuMap: Map<string, QContextMenu> = new Map();
 
  /**
   * Format the node filtering criteria to be used in step description object.
   *
   * @param nodeCriteria The node filtering criteria (e.g filter by color).
   * @returns The filtering text to be used in step description object.
   */
  public static formatStepDescriptionNodeCriteria(nodeCriteria?: GanttChartNodeSOPCriteria): string {
    const criteriaArr: string[] = [];
    if (nodeCriteria) {
      if (nodeCriteria.ColorHex) {
        criteriaArr.push(`ColorHex = ${nodeCriteria?.ColorHex}`);
      }
 
      // Extend with more criteria in future
    }
 
    // If node criteria undefined, meaning retrieve nodes without filtering thus no criteria text to return
    const criteriaTextResult = nodeCriteria ? ` Node filtering criteria: ${criteriaArr.join(', ')}.` : '';
 
    return criteriaTextResult;
  }
 
  public constructor(formName: string, componentPath: string, contextMenuName?: string) {
    super(formName, componentPath);
 
    if (contextMenuName) {
      this._contextMenu = new QContextMenu(contextMenuName);
    }
  }
 
  /**
   * Select multiple nodes (based on one or more index, 0-based) in a gantt chart row, right click and select context menu item.
   *
   * @param row Row to right click select nodes
   * @param indexes Index of nodes to select (0-based index)
   * @param contextMenuItem Context menu item to select
   */
  public async rightClickNodeIndexesAndSelectContextMenu(row: GanttChartRow, indexes: number[], contextMenuItem: ContextMenuItemSOP): Promise<void> {
    const nodes = await this.getAllNodes(row);
    for (const i of indexes) {
      const node = nodes[i];
      await this.rightClickNode(node, await node.getStartDate(), true);
    }
    await this._contextMenu.selectMenuItem(contextMenuItem.Name);
  }
 
  /**
   * Right click row and select menu in context menu.
   *
   * @param gcRow row to right click.
   * @param contextMenuItem Context menu item to select.
   */
  public async rightClickRowAndSelectMenu(gcRow: GanttChartRow, contextMenuItem: ContextMenuItemSOP): Promise<void> {
    const contextMenu = this.findCreateContextMenu(contextMenuItem.ContextMenu);
    await this.rightClickRow(gcRow);
 
    expect(await contextMenu!.isVisible()).toBe(true, 'ContextMenu on GanttChart shall be seen');
    await contextMenu!.selectMenuItem(contextMenuItem.Name);
  }
 
  /**
   * To find and verify the gantt chart node exists for certain start and end date.
   *
   * @param rowName The gantt chart row name.
   * @param start The start date of the node.
   * @param end The end date of the node.
   * @param nodeColor The background color of the node.
   */
  public async verifyNodeExists(rowName: string, start: Date, end: Date, nodeColor?: ColorSOP): Promise<void> {
    const rows = await this.getAllRows();
    let row: GanttChartRow | undefined;
 
    // Find matching row
    for (const r of rows) {
      const rTitle = await r.getTitle();
      // Remove HTML content to get the title
      if (getHtmlContent(rTitle)[0] === rowName) {
        row = r;
        break;
      }
    }
 
    if (row !== undefined) {
      const s = await (await row.getNode(0))?.getStartDate();
      const e = await (await row.getNode(0))?.getEndDate();
 
      expect(s?.getTime()).toBe(convertDateToUTC(start).getTime(), 'Start date is different.');
      expect(e?.getTime()).toBe(convertDateToUTC(end).getTime(), 'End date is different.');
 
      if (nodeColor) {
        const bgcolor = (await row.getNode(0))?.getBackgroundColor();
        expect(bgcolor).toBe(nodeColor.Hex, 'Node color is different.');
      }
    } else {
      fail(`Unable to find the gantt chart row ${  rowName}`);
    }
  }
 
  /**
   * Verifies if the gantt chart row has the expected number of nodes.
   *
   * @param gcRow The row to check if it has the expected number of nodes.
   * @param expectedNum The expected number of nodes.
   * @param nodeCriteria The criteria to count node
   */
  public async verifyNodeCount(gcRow: GanttChartRow, expectedNum: number, nodeCriteria?: GanttChartNodeSOPCriteria): Promise<void> {
    let numNodes = 0;
    if (nodeCriteria) {
      const nodes = await this.getAllNodes(gcRow);
      for (const node of nodes) {
        if (nodeCriteria.ColorHex && nodeCriteria.ColorHex === (await node.getBackgroundColor())) {
          numNodes++;
        }
      }
    } else {
      numNodes = await gcRow.getNumNodes();
    }
    expect(numNodes).toBe(expectedNum, `Verify fail for retrieving node count.${GanttChartSOP.formatStepDescriptionNodeCriteria(nodeCriteria)}`);
  }
 
  /**
   * Verifies when node is right clicked, the context menu item is disabled and shows the expected tooltip.
   *
   * @param rowTitle Gantt chart row title
   * @param nodeStartOrIndex Node index number (to enhance with start in future if needed)
   * @param contextMenuItem Context menu item that should be disabled
   * @param tooltip Tooltip expected when context menu item is about to be selected
   */
  public async verifyNodeContextMenuItemDisabled(rowTitle: string, nodeStartOrIndex: number, contextMenuItem: ContextMenuItemSOP, tooltip: string): Promise<void> {
    const contextMenu = this.findCreateContextMenu(contextMenuItem.ContextMenu);
 
    const row = (await this.getRowsByTitle(rowTitle))[0];
    const node = (await row.getNode(nodeStartOrIndex)) as GanttChartNode;
 
    if (this.verifyNodeDefined(node, rowTitle, nodeStartOrIndex)) {
      await this.rightClickNode(node);
 
      const [isClickable, actualToolTip] = await ContextMenuSOP.verifyIsMenuItemClickable(contextMenu!, contextMenuItem.Name, true);
      expect(isClickable).toBe(false, `Expected context menu "${contextMenuItem.Label}" disabled for node index ${nodeStartOrIndex} in row = "${rowTitle}".`);
      expect(actualToolTip).toBe(tooltip, `Verify tooltip fail for context menu "${contextMenuItem.Label}" on node index ${nodeStartOrIndex} in row = "${rowTitle}"."`);
    }
  }
 
  /**
   * Verifies when row is right clicked, the context menu item is disabled and shows the expected tool tip.
   *
   * @param rowTitle Row title to right click and check
   * @param contextMenuItem Context menu item that should be disabled
   * @param tooltip Tooltip expected when context menu item is about to be selected
   * @param tooltipExactMatch true by default. If true, the expected tooltip is to be exactly the same. Else, the expected tooltip is expected to contain the tooltip parameter.
   */
  public async verifyRowContextMenuItemDisabled(rowTitle: string, contextMenuItem: ContextMenuItemSOP, tooltip: string, tooltipExactMatch: boolean = true): Promise<void> {
    const contextMenu = this.findCreateContextMenu(contextMenuItem.ContextMenu);
 
    const row = (await this.getRowsByTitle(rowTitle))[0];
    await this.rightClickRow(row);
 
    const [isClickable, actualToolTip] = await ContextMenuSOP.verifyIsMenuItemClickable(contextMenu!, contextMenuItem.Name, true);
    expect(isClickable).toBe(false, `Expected context menu "${contextMenuItem.Label}" disabled for row = "${rowTitle}".`);
    if (tooltipExactMatch) {
      expect(actualToolTip).toBe(tooltip, `Verify tooltip fail for context menu "${contextMenuItem.Label}" on row = "${rowTitle}".`);
    } else {
      expect(actualToolTip).toContain(tooltip, `Verify tooltip fail for context menu "${contextMenuItem.Label}" on row = "${rowTitle}".`);
    }
  }
 
  /**
   * Throws error if gantt chart node is undefined. Use in method to verify before proceeding with more node related actions.
   *
   * @param node Node to verify.
   * @param rowTitle Gantt chart row title, to be used in error message if node is undefined.
   * @param nodeStartOrIndex Gantt chart node start time or index, to be used in error message if node is undefined.
   * @returns True if node is defined, else method will already throw error before reach return statement.
   */
  private verifyNodeDefined(node: GanttChartNode | undefined, rowTitle: string, nodeStartOrIndex: number): boolean {
    if (node === undefined) {
      throw Error(`Cannot find gantt chart node with row title = "${rowTitle}" and node index = "${nodeStartOrIndex}".`);
    }
 
    return true;
  }
 
  public async verifyRowExists(rowTitle: string): Promise<void> {
    const rows = await this.getRowsByTitle(rowTitle);
    expect(rows.length === 1).toBe(true, `Cannot find gantt chart row with title "${rowTitle}".`);
  }
 
  /**
   * Returns the context menu from stored map if previously created, else create, store and return it.
   *
   * @param name Context menu name.
   * @returns Context menu from stored map.
   */
  private findCreateContextMenu(name: string): QContextMenu | undefined {
    let cmMenu: QContextMenu | undefined;
    if (this._cmMenuMap.has(name)) {
      cmMenu = this._cmMenuMap.get(name);
    } else {
      cmMenu = new QContextMenu(name);
      this._cmMenuMap.set(name, cmMenu);
    }
 
    return cmMenu;
  }
}
 
/**
 * One or more criteria defining the gantt chart node. To be used when verifying if node(s) fulfills the criteria.
 * Example: verify number of nodes that are blue in color.
 */
export interface GanttChartNodeSOPCriteria {
  ColorHex?: string;
}
 
// Step description to re-use in spec file to prevent scriptor re-write each time
const stepGanttChart = {
  ensureVisible: (gcName: string, startDate: string): string => `In gantt chart "${gcName}", scroll gantt chart time such that date "${startDate}" is visible.`,
  rightClickNodeIndexesAndSelectContextMenu: (gcName: string, gcRowTitle: string, indexes: number[], menuLabel: string): string => `In gantt chart "${gcName}" and in row "${gcRowTitle}", right click node(s) with index "${indexes}" and select menu "${menuLabel}".`,
  rightClickRowAndSelectContextMenu: (gcName: string, gcRowTitle: string, menuLabel: string): string => `In gantt chart "${gcName}", right click row "${gcRowTitle}" and select menu "${menuLabel}".`,
  verifyNodeExists: (gcName: string, gcRowTitle: string, start: Date, end: Date, nodeColor?: string): string => {
    const verifyColor = nodeColor ? ` Verify node color = "${nodeColor}".` : '';
    return `In gantt chart "${gcName}", verify node exists for row = "${gcRowTitle}", start = "${start.toString()}" and end = "${end.toString()}".${verifyColor}`;
  },
  verifyNodeValue: (gcName: string, gcRowTitle: string, start: string, end: string, expectedValue: string): string => `In gantt chart "${gcName}", verify node value = ${expectedValue} for row = "${gcRowTitle}", start = "${start}" and end = "${end}".`,
  verifyNodeContextMenuItemDisabled: (gcName: string, gcRowTitle: string, gcNodeIndex: string, menuLabel: string, tooltip: string): string =>
    `In gantt chart "${gcName}", verify node at row "${gcRowTitle}" and node index "${gcNodeIndex}" when right clicked, menu item "${menuLabel}" is disabled and shows tooltip "${tooltip}".`,
  verifyNodeCount: (gcName: string, gcRowTitle: string, expectedNum: number, nodeCriteria?: GanttChartNodeSOPCriteria): string =>
    `In gantt chart "${gcName}", verify number of nodes in row "${gcRowTitle}" to be "${expectedNum}".${GanttChartSOP.formatStepDescriptionNodeCriteria(nodeCriteria)}`,
  verifyRowContextMenuItemDisabled: (gcName: string, gcRowTitle: string, label: string, tooltip: string): string =>
    `In gantt chart "${gcName}", verify when row "${gcRowTitle}" is right clicked, menu item "${label}" is disabled and shows tooltip "${tooltip}".`,
  verifyRowExists: (gcName: string, rowTitle: string): string => `In gantt chart "${gcName}", verify row "${rowTitle}" exists.`,
};
 
export { stepGanttChart as StepGanttChart };