/// <reference types="chai" />
import Suite from '../Suite';
import Test from '../Test';
import { Handle } from '@dojo/interfaces/core';
import Task from '@dojo/core/async/Task';
import ErrorFormatter, { ErrorFormatOptions } from '../common/ErrorFormatter';
import Reporter, { ReporterOptions } from '../reporters/Reporter';
import { ObjectInterface } from '../interfaces/object';
import { TddInterface } from '../interfaces/tdd';
import { BddInterface } from '../interfaces/bdd';
import { BenchmarkInterface } from '../interfaces/benchmark';
import { BenchmarkReporterOptions } from '../reporters/Benchmark';
import Promise from '@dojo/shim/Promise';
import { RuntimeEnvironment } from '../types';
/**
 * This is the default executor class.
 */
export default abstract class Executor<E extends Events = Events, C extends Config = Config, P extends Plugins = Plugins> {
    protected _assertions: {
        [name: string]: any;
    };
    protected _config: C;
    protected _rootSuite: Suite;
    protected _errorFormatter: ErrorFormatter;
    protected _hasSuiteErrors: boolean;
    protected _hasTestErrors: boolean;
    protected _interfaces: {
        [name: string]: any;
    };
    protected _loader: Loader;
    protected _loaderConfig: any;
    protected _loaderInit: Promise<Loader>;
    protected _autoLoadingPlugins: boolean;
    protected _loadingPlugins: {
        name: string;
        init: Task<void>;
    }[];
    protected _loadingPluginOptions: any | undefined;
    protected _listeners: {
        [event: string]: Listener<any>[];
    };
    protected _plugins: {
        [name: string]: any;
    };
    protected _reporters: Reporter[];
    protected _runTask: Task<void>;
    constructor(options?: {
        [key in keyof C]?: any;
    });
    /**
     * Get the current enviroment
     */
    readonly abstract environment: RuntimeEnvironment;
    /**
     * The resolved configuration for this executor.
     */
    readonly config: C;
    /**
     * The root suites managed by this executor
     */
    readonly suites: Suite[];
    /**
     * Format an error, normalizing the stack trace and resolving source map references
     */
    formatError(error: Error, options?: ErrorFormatOptions): string;
    /**
     * Load a script or scripts. This is a convenience method for loading and evaluating simple scripts, not necessarily
     * modules. If multiple script paths are provided, scripts will be loaded sequentially in the order given.
     *
     * @param script a path to a script
     */
    abstract loadScript(script: string | string[]): Task<void>;
    /**
     * Add a suite to the set of suites that will be run when `run` is called. The suited is added by calling a factory
     * function. The use of a factory allows for distinct instances of a given suite to be create when an executor has
     * multiple root suites.
     * parent suites.
     *
     * @param factory A function that will create a Suite object and add it to a given parent suite
     */
    addSuite(factory: (parentSuite: Suite) => void): void;
    /**
     * Update this executor's configuration.
     */
    configure(options: {
        [key in keyof C]?: any;
    }): void;
    /**
     * Emit an event to all registered listeners.
     *
     * Event listeners may execute async code, and a failing handler (one that rejects or throws an error) will cause the
     * emit to fail.
     */
    emit(eventName: 'afterRun'): Task<void>;
    emit(eventName: 'beforeRun'): Task<void>;
    emit(eventName: 'runStart'): Task<void>;
    emit(eventName: 'runEnd'): Task<void>;
    emit<T extends keyof E>(eventName: T, data: E[T]): Task<void>;
    /**
     * Get a registered interface plugin
     */
    getInterface(name: 'object'): ObjectInterface;
    getInterface(name: 'tdd'): TddInterface;
    getInterface(name: 'bdd'): BddInterface;
    getInterface(name: 'benchmark'): BenchmarkInterface;
    /**
     * Get any resources registered by a particular plugin.
     */
    getPlugin<Y extends keyof P>(type: Y, name: string): P[Y];
    getPlugin(name: 'chai'): Chai.ChaiStatic;
    getPlugin(name: 'interface.object'): ObjectInterface;
    getPlugin(name: 'interface.tdd'): TddInterface;
    getPlugin(name: 'interface.bdd'): BddInterface;
    getPlugin(name: 'interface.benchmark'): BenchmarkInterface;
    getPlugin<T>(name: string): T;
    /**
     * Convenience method for emitting log events
     */
    log(...args: any[]): Task<void>;
    /**
     * Add a listener for a test event. When an event is emitted, the executor will wait for all Promises returned by
     * listener callbacks to resolve before continuing.
     */
    on<T extends keyof E>(eventName: T, listener: Listener<E[T]>): Handle;
    on(listener: Listener<{
        name: string;
        data?: any;
    }>): Handle;
    /**
     * Register an interface plugin
     */
    registerInterface(name: string, iface: any): void;
    /**
     * Set the loader script that will be used to load plugins and suites.
     * will handle the loading of test suites.
     */
    registerLoader(init: LoaderInit): void;
    /**
     * Register a plugin that will be loaded at the beginning of the testing process, after the loader but before any
     * suites are registered.
     */
    registerPlugin<T extends keyof P>(type: T, name: string, init: PluginInitializer<P[T]>): void;
    registerPlugin(name: string, init: PluginInitializer): void;
    /**
     * Register a reporter plugin
     */
    registerReporter(name: string, Ctor: typeof Reporter): void;
    /**
     * Run tests. This method sets up the environment for test execution, runs the tests, and runs any finalization code
     * afterwards. Subclasses should override `_beforeRun`, `_runTests`, and `_afterRun` to alter how tests are run.
     */
    run(): Task<void>;
    /**
     * Code to execute after the main test run has finished to shut down the test system.
     */
    protected _afterRun(): Task<void>;
    /**
     * Add a resolved plugin to the internal plugins list
     */
    protected _assignPlugin(name: string, plugin: any): void;
    /**
     * Code to execute before the main test run has started to set up the test system. This is where Executors can do
     * any last-minute configuration before the testing process begins.
     *
     * This method returns a Task that resolves to a boolean. A value of true indicates that Intern should skip running
     * tests and exit normally.
     */
    protected _beforeRun(): Task<boolean>;
    protected _emitCoverage(source?: string): Task<void> | undefined;
    /**
     * Evaluate a config property key
     */
    protected _evalProperty(key: string): {
        name: string;
        addToExisting: boolean;
    };
    /**
     * Load a loader
     */
    protected _loadLoader(): Promise<void>;
    /**
     * Load plugins
     */
    protected _loadPlugins(): Task<void>;
    /**
     * Load scripts in the `require` list. These will be loaded sequentially in order using a platform-specific loading
     * mechanism (script injection or Node's require).
     */
    protected _loadRequires(): void;
    /**
     * Load suites
     */
    protected _loadSuites(): Task<void>;
    /**
     * Process an arbitrary config value. Subclasses can override this method to pre-process arguments or handle them
     * instead of allowing Executor to.
     */
    protected _processOption(name: keyof C, value: any, addToExisting: boolean): void;
    /**
     * Set an option value.
     */
    protected _setOption(name: keyof C, value: any, addToExisting?: boolean, config?: C): void;
    /**
     * Resolve the config object.
     */
    protected _resolveConfig(): Task<void>;
    /**
     * Runs each of the root suites, limited to a certain number of suites at the same time by `maxConcurrency`.
     */
    protected _runTests(): Task<void>;
}
export interface ExecutorConstructor<E extends Events, C extends Config, T extends Executor<E, C>> {
    new (config?: Partial<C>): T;
}
export { Handle };
export interface BenchmarkConfig extends BenchmarkReporterOptions {
    id: string;
}
export interface ResourceConfig {
    /**
     * The loader used to load test suites and application modules. When passed in as part of a config object, the
     * `loader` property can be a string with a loader name or the path to a loader script. It may also be an object
     * with `script` and `config` properties. Intern provides built-in loader scripts for Dojo and Dojo2, which can be
     * specified with the IDs 'dojo' and 'dojo2'.
     *
     * ```ts
     * loader: 'dojo2'
     * loader: 'tests/loader.js'
     * loader: { script: 'dojo', config: { packages: [ { name: 'app', location: './js' } ] } }
     * ```
     */
    loader: LoaderDescriptor;
    /**
     * A list of scripts to load before suites are loaded. These must be simple scripts, not modules, as a module loader
     * may not be available when these are loaded. Also, these scripts should be synchronous. If they need to run async
     * actions, they can register listeners for the 'runBefore' or 'runAfter' executor events.
     */
    plugins: PluginDescriptor[];
    /**
     * A list of reporter names or descriptors. These reporters will be loaded and instantiated before testing begins.
     */
    reporters: ReporterDescriptor[];
    /**
     * A list of scripts or modules to load before any loader, plugins, or suites.
     */
    require: string[];
    /** A list of paths to suite scripts (or some other suite identifier usable by the suite loader). */
    suites: string[];
}
export interface Config extends ResourceConfig {
    /** If true, Intern will exit as soon as any test fails. */
    bail: boolean;
    baseline: boolean;
    /** The path to the project base */
    basePath: string;
    benchmark: boolean;
    benchmarkConfig?: BenchmarkConfig;
    browser: ResourceConfig;
    /**
     * The global variable that will be used to store coverage data
     */
    coverageVariable: string;
    /** If true, emit and display debug messages. */
    debug: boolean;
    /** The default timeout for async tests, in ms. */
    defaultTimeout: number;
    /** A description for this test run */
    description: string;
    /** A regexp matching file names that shouldn't be instrumented, or `true` to disable instrumentation. */
    excludeInstrumentation: true | RegExp;
    /** If true, filter external library calls and runtime calls out of error stacks. */
    filterErrorStack: boolean;
    /** A regexp matching tests that should be run. It defaults to `/./` (which matches everything). */
    grep: RegExp;
    /** The path to Intern */
    internPath: string;
    /** A top-level name for this configuration. */
    name: string;
    node: ResourceConfig;
    /** An identifier for this test session. By default it will have the value ''. */
    sessionId: string;
    /** If true, display the resolved config and exit */
    showConfig: boolean;
}
export interface ReporterDescriptor {
    name: string;
    options?: ReporterOptions;
}
export interface PluginDescriptor {
    script: string;
    options?: any;
}
export interface Listener<T> {
    (arg: T): void | Promise<void>;
}
export interface CoverageMessage {
    sessionId?: string;
    source?: string;
    coverage: any;
}
export interface DeprecationMessage {
    original: string;
    replacement?: string;
    message?: string;
}
export interface ExecutorEvent {
    name: keyof Events;
    data: any;
}
export interface Events {
    '*': ExecutorEvent;
    /** Emitted after the local executor has finished running suites */
    afterRun: never;
    /** Emitted before the local executor loads suites */
    beforeRun: never;
    /** Coverage info has been gathered */
    coverage: CoverageMessage;
    /** A deprecated method was called */
    deprecated: DeprecationMessage;
    /** An unhandled error occurs */
    error: Error;
    /** A debug log event */
    log: string;
    /** All tests have finished running */
    runEnd: never;
    /** Emitted just before tests start running  */
    runStart: never;
    /** A new suite has been added */
    suiteAdd: Suite;
    /** A suite has fininshed running */
    suiteEnd: Suite;
    /** A suite has started running */
    suiteStart: Suite;
    /** A new test has been added */
    testAdd: Test;
    /** A test has finished */
    testEnd: Test;
    /** A test has started */
    testStart: Test;
    /** A non-fatal error occurred */
    warning: string;
}
/**
 * Known plugin types
 */
export interface Plugins {
    reporter: typeof Reporter;
}
/**
 * An async loader callback.
 */
export interface Loader {
    (modules: string[]): Promise<void>;
}
/**
 * A loader initialization function.
 */
export interface LoaderInit {
    (options: {
        [key: string]: any;
    }): Promise<Loader> | Loader;
}
export interface LoaderDescriptor {
    script: string;
    options?: {
        [key: string]: any;
    };
}
export interface PluginInitializer<T extends any = any> {
    (options?: {
        [key: string]: any;
    }): Task<T> | T;
}
