// Type definitions for spectron v3.6.0
// Project: https://github.com/electron/spectron
// Definitions by: deerares <https://github.com/deerares>

/// <reference types="node" />

declare module 'spectron' {
  import * as Electron from 'electron';
  import * as WebdriverIO from 'webdriverio';

  interface AccessibilityAuditOptions {
    /**
     * true to ignore failures with a severity of 'Warning' and only
     * include failures with a severity of 'Severe'. Defaults to false.
     */
    ignoreWarnings?: boolean;

    /**
     * Rule code values such as AX_COLOR_01 to ignore failures for.
     */
    ignoreRules?: string[];
  }

  interface AccessibilityAuditResult {
    /**
     * False when the audit has failures
     */
    failed: boolean;

    /**
     * A detailed message about the results
     */
    message: string;

    /**
     * An array of detail objects for each failed rule
     */
    results: {
      /**
       * A unique accessibility rule identifier
       */
      code: string;

      /**
       * Selector path of each HTML element that failed the rule
       */
      elements: string[];

      /**
       * A String message about the failed rule
       */
      message: string;

      /**
       * 'Warning' or 'Severe'
       */
      severity: 'Warning' | 'Severe';

      /**
       * URL providing more details about the failed rule
       */
      url: string;
    }[];
  }

  export interface SpectronClient extends WebdriverIO.Browser<'async'> {
    /**
     * Wait until the window is no longer loading.
     * Takes an optional timeout in milliseconds that defaults to 5000.
     */
    waitUntilWindowLoaded(timeout?: number): Promise<void>;

    /**
     * Wait until the element matching the given selector contains the given text.
     * Takes an optional timeout in milliseconds that defaults to 5000.
     */
    waitUntilTextExists(
      selector: string,
      text: string,
      timeout?: number
    ): Promise<void>;

    /**
     * Gets the number of open windows. <webview> tags are also counted as separate windows.
     */
    getWindowCount(): Promise<number>;
    /**
     * Focus a window using its index from the windowHandles() array.
     * <webview> tags can also be focused as a separate window.
     */
    windowByIndex(index: number): Promise<void>;
    /**
     * Get the selected text in the current window.
     */
    getSelectedText(): Promise<string>;
    /**
     * Gets the console log output from the render process.
     * The logs are cleared after they are returned.
     */
    getRenderProcessLogs(): Promise<object[]>;
    /**
     * Gets the console log output from the main process.
     * The logs are cleared after they are returned.
     */
    getMainProcessLogs(): Promise<string[]>;

    /**
     * Run an accessibility audit in the focused window with the specified options.
     */
    auditAccessibility(
      options?: AccessibilityAuditOptions
    ): Promise<AccessibilityAuditResult>;
  }

  export type SpectronWindow = {
    [P in keyof Electron.BrowserWindow]: Electron.BrowserWindow[P] extends (
      ...args: infer A
    ) => infer R
      ? (...args: A) => Promise<R>
      : undefined;
  };

  export interface SpectronWebContents extends Electron.WebContents {
    savePage(
      fullPath: string,
      saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML',
      callback?: (error: Error) => void
    ): boolean;
    savePage(
      fullPath: string,
      saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML'
    ): Promise<void>;
    savePage(
      fullPath: string,
      saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML'
    ): any;
    executeJavaScript(code: string, userGesture?: boolean): Promise<any>;
  }

  type BasicAppSettings = {
    /**
     * String host name of the launched chromedriver process. Defaults to 'localhost'.
     */
    host?: string;
    /**
     *  Number port of the launched chromedriver process. Defaults to 9515.
     */
    port?: number;
    /**
     * Number in milliseconds to wait for application quitting.
     * Defaults to 1000 milliseconds.
     */
    quitTimeout?: number;
    /**
     * Number in milliseconds to wait for ChromeDriver to start.
     * Defaults to 5000 milliseconds.
     */
    startTimeout?: number;
    /**
     *  Number in milliseconds to wait for calls like waitUntilTextExists and
     *  waitUntilWindowLoaded to complete.
     *  Defaults to 5000 milliseconds.
     */
    waitTimeout?: number;
    /**
     * Number of retry attempts to make when connecting to ChromeDriver.
     * Defaults to 10 attempts.
     */
    connectionRetryCount?: number;
    /**
     * Number in milliseconds to wait for connections to ChromeDriver to be made.
     * Defaults to 30000 milliseconds.
     */
    connectionRetryTimeout?: number;
    /**
     * String path to a node executable to launch ChromeDriver with.
     * Defaults to process.execPath.
     */
    nodePath?: string;
    /** String path to the Electron application executable to launch.
     * Note: If you want to invoke electron directly with your app's main script then you should
     * specify path as electron via electron-prebuilt and specify your app's main script path as
     * the first argument in the args array.
     */
    path: string;
    /**
     * Array of arguments to pass to the Electron application.
     */
    args?: string[];
    /**
     * Array of arguments to pass to ChromeDriver.
     * See here (https://sites.google.com/chromium.org/driver/capabilities) for details
     * on the Chrome arguments.
     */
    chromeDriverArgs?: string[];
    /**
     * Object of additional environment variables to set in the launched application.
     */
    env?: object;
    /**
     *  String address of a Chrome debugger server to connect to.
     */
    debuggerAddress?: string;
    /**
     * String path to file to store ChromeDriver logs in.
     * Setting this option enables --verbose logging when starting ChromeDriver.
     */
    chromeDriverLogPath?: string;
    /**
     * String path to a directory where Webdriver will write logs to.
     * Setting this option enables verbose logging from Webdriver.
     */
    webdriverLogPath?: string;
    /**
     * Extra Webdriver options
     */
    webdriverOptions?: object;
    /**
     * Custom property name to use when requiring modules.
     * Defaults to require.
     * This should only be used if your application deletes the main window.require function
     * and assigns it to another property name on window.
     */
    requireName?: string;
  };
  type AppConstructorOptions = BasicAppSettings & {
    /**
     *  String path to the working directory to use for the launched application.
     *  Defaults to process.cwd().
     */
    cwd?: string;
  };
  export type ApplicationSettings = BasicAppSettings & {
    /**
     *  String path to the working directory to use for the launched application.
     *  Defaults to process.cwd().
     */
    workingDirectory?: string;
  };

  /**
   * Start and stop your Electron application.
   */
  export class Application {
    /**
     * Spectron uses WebdriverIO and exposes the managed client property on the created
     * Application instances.
     * The full client API provided by WebdriverIO can be found here
     * http://webdriver.io/api.html
     * Several additional commands are provided specific to Electron.
     */
    client: SpectronClient;
    /**
     * The electron property is your gateway to accessing the full Electron API.
     * Each Electron module is exposed as a property on the electron property so you can
     * think of it as an alias for require('electron') from within your app.
     */
    electron: typeof Electron;
    /**
     * The browserWindow property is an alias for require('electron').remote.getCurrentWindow().
     * It provides you access to the current BrowserWindow and contains all the APIs.
     * https://electron.atom.io/docs/api/browser-window/
     */
    browserWindow: SpectronWindow;
    /**
     * The webContents property is an alias for
     * require('electron').remote.getCurrentWebContents().
     * It provides you access to the WebContents for the current window
     * and contains all the APIs.
     * https://electron.atom.io/docs/api/web-contents/
     */
    webContents: SpectronWebContents;
    /**
     * The mainProcess property is an alias for require('electron').remote.process.
     * It provides you access to the main process's process global.
     * https://nodejs.org/api/process.html
     */
    mainProcess: NodeJS.Process;
    /**
     *The rendererProcess property is an alias for global.process.
     * It provides you access to the main process's process global.
     * https://nodejs.org/api/process.html
     */
    rendererProcess: NodeJS.Process;

    constructor(options: AppConstructorOptions);

    /**
     * Starts the application.
     * Returns a Promise that will be resolved when the application is ready to use.
     * You should always wait for start to complete before running any commands.
     */
    start(): Promise<Application>;

    /**
     * Stops the application.
     * Returns a Promise that will be resolved once the application has stopped.
     */
    stop(): Promise<Application>;

    /**
     * Stops the application and then starts it.
     * Returns a Promise that will be resolved once the application has started again.
     */
    restart(): Promise<Application>;

    /**
     * Checks to determine if the application is running or not.
     */
    isRunning(): boolean;

    /**
     * Get all the configured options passed to the new Application() constructor.
     * This will include the default options values currently being used.
     */
    getSettings(): ApplicationSettings;
  }
}
