/** 
 | 
 * @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()); 
 | 
    } 
 | 
  } 
 | 
} 
 |