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
/**
 * @file        S&OP DurationSelector component to wrap common methods the team encounter during development
 * @description DurationSelector class extending e2elib's DurationSelector.
 * All S&OP page objects inherit from our own class (inheriting e2e/libappbase), but we can propose common methods to them.
 * @author      Clarence (clarence.chan@3ds.com)
 * @copyright   Dassault Systèmes
 */
import { QUtils } from '../e2elib/lib/src/main/qutils.class';
import { DurationSelector } from '../e2elib/lib/src/pageobjects/durationselector.component';
import { ElementFinder } from '../e2elib/node_modules/protractor/built';
import { UIActionSOP } from './objectsop';
 
/**
 * Customize duration interface. All fields are optional to allow setting/getting parts of the duration only (e.g set days and hours only).
 */
export interface DurationInput {
  Days?: number;
  Hours?: number;
  Minutes?: number;
  Seconds?: number;
}
 
/**
 * Helper class to format duration input string and convert from string to DurationInput interface.
 */
export class DurationInputHelper {
  /**
   * Format the string to represent a DurationInput interface.
   * The returned string NOT to be used directly but always use methods from this class to convert between formats (as internal representation are allowed to change).
   *
   * @param input DurationInput object to represent the duration portions to be set.
   * @returns The duration input string.
   */
  public static getDurationString(input: DurationInput): string {
    let value = 'D:H:M:S';
 
    // Must explicit check instead of using shorthand if(input.Days)
    if (input.Days !== undefined) {
      value = value.replace('D', input.Days.toString());
    }
 
    if (input.Hours !== undefined) {
      value = value.replace('H', input.Hours.toString());
    }
 
    if (input.Minutes !== undefined) {
      value = value.replace('M', input.Minutes.toString());
    }
 
    if (input.Seconds !== undefined) {
      value = value.replace('S', input.Seconds.toString());
    }
 
    return value;
  }
 
  /**
   * Convert duration input string to a DurationInput interface. Check each DurationInput property to be present before accessing its value as all properties are marked optional.
   *
   * @param value The duration input string retrived via DurationInputHelper.getDurationString method.
   * @returns The DurationInput object representing a duration.
   */
  public static getDurationInput(value: string): DurationInput {
    const tokens = value.split(':');
    const durationInput: DurationInput = {};
 
    if (tokens[0] !== 'D') {
      durationInput.Days = (tokens[0] as unknown) as number; // Convert to unknown first as there's warning before casting to number
    }
 
    if (tokens[1] !== 'H') {
      durationInput.Hours = (tokens[1] as unknown) as number;
    }
 
    if (tokens[2] !== 'M') {
      durationInput.Minutes = (tokens[2] as unknown) as number;
    }
 
    if (tokens[3] !== 'S') {
      durationInput.Seconds = (tokens[3] as unknown) as number;
    }
 
    return durationInput;
  }
}
 
export class DurationSelectorSOP extends DurationSelector implements UIActionSOP {
  private readonly _durationFormat: DurationInput;
 
  public constructor(componentPath: string, durationFormat: DurationInput, isCustomPath?: boolean, elementObj?: ElementFinder) {
    super(componentPath, isCustomPath, elementObj);
    this._durationFormat = durationFormat;
  }
 
  public async clickActionLinkText(expectedActionLinkText: string): Promise<void> {
    const isActionLinkMatch = await this.hasActionLink() && await this.getActionLinkText() === expectedActionLinkText;
    expect(isActionLinkMatch).toBe(true, `Unable to proceed click action link for duration selector ${await this.getComponentLabel()}. Expecting action link text '${expectedActionLinkText}'.`);
 
    if (isActionLinkMatch) {
      await this.clickActionLink();
    }
  }
 
  public async getValueString(): Promise<string> {
    const days = Number(await this.getDay());
    const hours = Number(await this.getHour());
    const minutes = Number(await this.getMinute());
    const seconds = Number(await this.getSecond());
    const inputDuration: DurationInput = {Days: days, Hours: hours, Minutes: minutes, Seconds: seconds};
    return DurationInputHelper.getDurationString(inputDuration);
  }
 
  public async setValue(value: string): Promise<void> {
    if (await this.isDisabled() && await this.hasActionLink()) {
      await this.clickActionLink();
    }
 
    await this.setDurationParts(value);
  }
 
  public async toggleValue(): Promise<void> {
    // Get current values for each duration parts
    const days = Number(await this.getDay());
    const hours = Number(await this.getHour());
    const minutes = Number(await this.getMinute());
    const seconds = Number(await this.getSecond());
 
    // Increment every parts of the duration (from days to seconds) so that cater for all scenarios to have duration UI to be changed value
    // This way, no need to check if UI is showing the part before incrementing as we manipulating Date object itself
    const incrementValue = 1;
    // Must use UTC methods else timezone makes querying value not accurate
    // Month argument is 0-based, 0 = January
    const d = new Date(Date.UTC(1970, 0, 1));
    d.setUTCDate(d.getUTCDate() + days + incrementValue);
    d.setUTCHours(d.getUTCHours() + hours + incrementValue);
    d.setUTCMinutes(d.getUTCMinutes() + minutes + incrementValue);
    d.setUTCSeconds(d.getUTCSeconds() + seconds + incrementValue);
 
    // Need build DurationInput interface to only contain what UI is displaying
    const currentDuration: DurationInput = {};
    if (this._durationFormat.Days !== undefined) {
      currentDuration.Days = d.getUTCDate() - 1; // As date initialized with 1st Jan 1970 thus we need subtract it out
    }
 
    if (this._durationFormat.Hours !== undefined) {
      currentDuration.Hours = d.getUTCHours();
    }
 
    if (this._durationFormat.Minutes !== undefined) {
      currentDuration.Minutes = d.getUTCMinutes();
    }
 
    if (this._durationFormat.Seconds !== undefined) {
      currentDuration.Seconds = d.getUTCSeconds();
    }
 
    await this.setValue(DurationInputHelper.getDurationString(currentDuration));
  }
 
  public async verifyBatchEditEnabled(expectedEnable: boolean, expectedActionLinkText: string): Promise<void> {
    const isBatchEditEnableDisableOK = await this.hasActionLink() &&
                                       await this.getActionLinkText() === expectedActionLinkText &&
                                       !await this.isDisabled() === expectedEnable;
    expect(isBatchEditEnableDisableOK).toBe(true, `Verify duration selector ${await this.getComponentLabel()} supports batch edit (expected enable = ${expectedEnable} & action link = ${expectedActionLinkText}).`);
  }
 
  public async verifyHasMaskError(expectedValue: boolean): Promise<void> {
    // Implements UIActionSOP
    const actualHasMaskError = await this.hasMaskError(expectedValue);
    expect(actualHasMaskError).toBe(expectedValue, `Verify has mask error for duration selector ${await this.getComponentLabel()}.`);
  }
 
  public async verifyTooltip(expectedValue: string): Promise<void> {
    const tooltip = await QUtils.getTooltip(this.tooltipLabelElement, true, true, true);
    expect(tooltip).toBe(expectedValue, `Hover ${await this.getComponentLabel()} to verify tooltip.`);
  }
 
  public async verifyEnabled(expectedEnable: boolean): Promise<void> {
    expect(!await this.isDisabled()).toBe(expectedEnable, `Verify enable state for duration selector ${await this.getComponentLabel()}.`);
  }
 
  public async verifyValue(expectedValue: string): Promise<void> {
    const expectedDuration: DurationInput =  DurationInputHelper.getDurationInput(expectedValue);
    let expectedDurationStr = '';
    let actualDurationStr = '';
 
    if (expectedDuration.Days) {
      const actualDays = await this.getDay();
      // Convert days to number as getDay() returns 00 string format (thus later problem if compare 0 to 00 which results false)
      actualDurationStr = `${Number(actualDays)} days`;
      expectedDurationStr = `${expectedDuration.Days.toString()} days`;
    }
 
    if (expectedDuration.Hours) {
      const actualHours = await this.getHour();
      actualDurationStr = `${actualDurationStr}, ${Number(actualHours)} hours`;
      expectedDurationStr = `${expectedDurationStr}, ${expectedDuration.Hours.toString()} hours`;
    }
 
    if (expectedDuration.Minutes) {
      const actualMinutes = await this.getMinute();
      actualDurationStr = `${actualDurationStr}, ${Number(actualMinutes)} minutes`;
      expectedDurationStr = `${expectedDurationStr}, ${expectedDuration.Minutes.toString()} minutes`;
    }
 
    if (expectedDuration.Seconds) {
      const actualSeconds = await this.getSecond();
      actualDurationStr = `${actualDurationStr}, ${Number(actualSeconds)} seconds`;
      expectedDurationStr = `${expectedDurationStr}, ${expectedDuration.Seconds.toString()} seconds`;
    }
 
    expect(actualDurationStr).toBe(expectedDurationStr, `Verify duration selector ${await this.getComponentLabel()}.`);
  }
 
  public async verifyVisible(expectedValue: string): Promise<void> {
    expect(await this.isVisible()).toBe(expectedValue.toLowerCase() === 'true', `Verify visible state for duration selector ${await this.getComponentLabel()}.`);
  }
 
  private async setDurationParts(value: string): Promise<void> {
    const duration: DurationInput =  DurationInputHelper.getDurationInput(value);
 
    if (duration.Days) {
      await this.setDay(duration.Days.toString());
    }
 
    if (duration.Hours) {
      await this.setHour(duration.Hours.toString());
    }
 
    if (duration.Minutes) {
      await this.setMinute(duration.Minutes.toString());
    }
 
    if (duration.Seconds) {
      await this.setSecond(duration.Seconds.toString());
    }
  }
}