// Type definitions for QUnit v1.16
// Project: http://qunitjs.com/
// Definitions by: Diullei Gomes <https://github.com/diullei>
// Definitions: https://github.com/borisyankov/DefinitelyTyped


interface DoneCallbackObject {
    /**
    * The number of failed assertions
    */
    failed: number;

    /**
    * The number of passed assertions
    */
    passed: number;

    /**
    * The total number of assertions
    */
    total: number;

    /**
    * The time in milliseconds it took tests to run from start to finish.
    */
    runtime: number;
}

interface LogCallbackObject {
    /**
    * The boolean result of an assertion, true means passed, false means failed.
    */
    result: boolean;

    /**
    * One side of a comparision assertion. Can be undefined when ok() is used.
    */
    actual: Object;

    /**
    * One side of a comparision assertion. Can be undefined when ok() is used.
    */
    expected: Object;

    /**
    * A string description provided by the assertion.
    */
    message: string;

    /**
    * The associated stacktrace, either from an exception or pointing to the source 
    * of the assertion. Depends on browser support for providing stacktraces, so can be 
    * undefined.
    */
    source: string;
}

interface ModuleStartCallbackObject {
    /**
    * Name of the next module to run
    */
    name: string;
}

interface ModuleDoneCallbackObject {
    /**
    * Name of this module
    */
    name: string;

    /**
    * The number of failed assertions
    */
    failed: number;

    /**
    * The number of passed assertions
    */
    passed: number;

    /**
    * The total number of assertions
    */
    total: number;
}

interface TestDoneCallbackObject {
    /**
    * TName of the next test to run
    */
    name: string;

    /**
    * Name of the current module
    */
    module: string;

    /**
    * The number of failed assertions
    */
    failed: number;

    /**
    * The number of passed assertions
    */
    passed: number;

    /**
    * The total number of assertions
    */
    total: number;

    /**
    * The total runtime, including setup and teardown
    */
    duration: number;
}

interface TestStartCallbackObject {
    /**
    * Name of the next test to run
    */
    name: string;

    /**
    * Name of the current module
    */
    module: string;
}

interface Config {
    altertitle: boolean;
    autostart: boolean;
    current: Object;
    reorder: boolean;
    requireExpects: boolean;
    testTimeout: number;
    urlConfig: Array<URLConfigItem>;
    done: any;
}

interface URLConfigItem {
    id: string;
    label: string;
    tooltip: string;
}

interface LifecycleObject {
    /**
     * Runs before each test
     * @deprecated
     */
    setup?: () => void;

    /**
     * Runs after each test
     * @deprecated
     */
    teardown?: () => void;
    /**
     * Runs before each test
     */
    beforeEach?: () => void;
    /**
     * Runs after each test
     */
    afterEach?: () => void;

    /**
     * Any additional properties on the hooks object will be added to that context.
     */
    [property: string]: any;
}

interface QUnitAssert {
    /* ASSERT */
    assert: any;
    current_testEnvironment: any;
    jsDump: any;

    /**
    * Instruct QUnit to wait for an asynchronous operation.
    * 
    * When your test has any asynchronous exit points, call assert.async() to get a unique
    * resolution callback for each async operation. The callback returned from assert.async()
    * will throw an Error if is invoked more than once.
    */
    async(): () => void;

    /**
    * A deep recursive comparison assertion, working on primitive types, arrays, objects, 
    * regular expressions, dates and functions.
    *
    * The deepEqual() assertion can be used just like equal() when comparing the value of 
    * objects, such that { key: value } is equal to { key: value }. For non-scalar values, 
    * identity will be disregarded by deepEqual.
    *
    * @param actual Object or Expression being tested
    * @param expected Known comparison value
    * @param message A short description of the assertion
    */
    deepEqual(actual: any, expected: any, message?: string): any;

    /** 
    * A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
    *
    * The equal assertion uses the simple comparison operator (==) to compare the actual 
    * and expected arguments. When they are equal, the assertion passes: any; otherwise, it fails. 
    * When it fails, both actual and expected values are displayed in the test result, 
    * in addition to a given message.
    * 
    * @param actual Expression being tested
    * @param expected Known comparison value
    * @param message A short description of the assertion
    */
    equal(actual: any, expected: any, message?: string): any;

    /**
    * Specify how many assertions are expected to run within a test.
    *
    * To ensure that an explicit number of assertions are run within any test, use 
    * expect( number ) to register an expected count. If the number of assertions 
    * run does not match the expected count, the test will fail.
    *
    * @param amount Number of assertions in this test.
    */
    expect(amount: number): any;

    /**
    * An inverted deep recursive comparison assertion, working on primitive types, 
    * arrays, objects, regular expressions, dates and functions.
    *
    * The notDeepEqual() assertion can be used just like equal() when comparing the 
    * value of objects, such that { key: value } is equal to { key: value }. For non-scalar 
    * values, identity will be disregarded by notDeepEqual.
    * 
    * @param actual Object or Expression being tested
    * @param expected Known comparison value
    * @param message A short description of the assertion
    */
    notDeepEqual(actual: any, expected: any, message?: string): any;

    /**
    * A non-strict comparison assertion, checking for inequality.
    *
    * The notEqual assertion uses the simple inverted comparison operator (!=) to compare 
    * the actual and expected arguments. When they aren't equal, the assertion passes: any; 
    * otherwise, it fails. When it fails, both actual and expected values are displayed 
    * in the test result, in addition to a given message.
    * 
    * @param actual Expression being tested
    * @param expected Known comparison value
    * @param message A short description of the assertion
    */
    notEqual(actual: any, expected: any, message?: string): any;

    notPropEqual(actual: any, expected: any, message?: string): any;

    propEqual(actual: any, expected: any, message?: string): any;

    /**
    * A non-strict comparison assertion, checking for inequality.
    *
    * The notStrictEqual assertion uses the strict inverted comparison operator (!==) 
    * to compare the actual and expected arguments. When they aren't equal, the assertion 
    * passes: any; otherwise, it fails. When it fails, both actual and expected values are 
    * displayed in the test result, in addition to a given message.
    * 
    * @param actual Expression being tested
    * @param expected Known comparison value
    * @param message A short description of the assertion
    */
    notStrictEqual(actual: any, expected: any, message?: string): any;

    /**
    * A boolean assertion, equivalent to CommonJS’s assert.ok() and JUnit’s assertTrue(). 
    * Passes if the first argument is truthy.
    *
    * The most basic assertion in QUnit, ok() requires just one argument. If the argument 
    * evaluates to true, the assertion passes; otherwise, it fails. If a second message 
    * argument is provided, it will be displayed in place of the result.
    * 
    * @param state Expression being tested
    * @param message A short description of the assertion
    */
    ok(state: any, message?: string): any;

    /**
    * A strict type and value comparison assertion.
    *
    * The strictEqual() assertion provides the most rigid comparison of type and value with 
    * the strict equality operator (===)
    * 
    * @param actual Expression being tested
    * @param expected Known comparison value
    * @param message A short description of the assertion
    */
    strictEqual(actual: any, expected: any, message?: string): any;

    /**
    * Assertion to test if a callback throws an exception when run.
    * 
    * When testing code that is expected to throw an exception based on a specific set of 
    * circumstances, use throws() to catch the error object for testing and comparison.
    * 
    * @param block Function to execute
    * @param expected Error Object to compare
    * @param message A short description of the assertion
    */
    throws(block: () => any, expected: any, message?: string): any;

    /**
    * @param block Function to execute
    * @param message A short description of the assertion
    */
    throws(block: () => any, message?: string): any;

    /**
    * Alias of throws.
    * 
    * In very few environments, like Closure Compiler, throws is considered a reserved word
    * and will cause an error. For that case, an alias is bundled called raises. It has the
    * same signature and behaviour, just a different name.
    * 
    * @param block Function to execute
    * @param expected Error Object to compare
    * @param message A short description of the assertion
    */
    raises(block: () => any, expected: any, message?: string): any;

    /**
    * Alias of throws.
    * 
    * In very few environments, like Closure Compiler, throws is considered a reserved word
    * and will cause an error. For that case, an alias is bundled called raises. It has the
    * same signature and behaviour, just a different name.
    * 
    * @param block Function to execute
    * @param message A short description of the assertion
    */
    raises(block: () => any, message?: string): any;
}

interface QUnitStatic extends QUnitAssert {    
    /* ASYNC CONTROL */

    /**
    * Start running tests again after the testrunner was stopped. See stop().
    *
    * When your async test has multiple exit points, call start() for the corresponding number of stop() increments.
    * 
    * @param decrement Optional argument to merge multiple start() calls into one. Use with multiple corrsponding stop() calls.
    */
    start(decrement?: number): any;

    /**
    * Stop the testrunner to wait for async tests to run. Call start() to continue.
    *
    * When your async test has multiple exit points, call stop() with the increment argument, corresponding to the number of start() calls you need.
    *
    * On Blackberry 5.0, window.stop is a native read-only function. If you deal with that browser, use QUnit.stop() instead, which will work anywhere.
    *
    * @param decrement Optional argument to merge multiple stop() calls into one. Use with multiple corrsponding start() calls.
    */
    stop(increment? : number): any;
    
    /* CALLBACKS */

    /**
    * Register a callback to fire whenever the test suite begins.
    *
    * QUnit.begin() is called once before running any tests. (a better would've been QUnit.start, 
    * but thats already in use elsewhere and can't be changed.)
    *
    * @param callback Callback to execute
    */
    begin(callback: () => any): any;

    /**
    * Register a callback to fire whenever the test suite ends.
    *
    * @param callback Callback to execute.
    */
    done(callback: (details: DoneCallbackObject) => any): any;

    /**
    * Register a callback to fire whenever an assertion completes.
    *
    * This is one of several callbacks QUnit provides. Its intended for integration scenarios like 
    * PhantomJS or Jenkins. The properties of the details argument are listed below as options.
    *
    * @param callback Callback to execute.
    */
    log(callback: (details: LogCallbackObject) => any): any;

    /**
    * Register a callback to fire whenever a module ends.
    *
    * @param callback Callback to execute.
    */
    moduleDone(callback: (details: ModuleDoneCallbackObject) => any): any;

    /**
    * Register a callback to fire whenever a module begins.
    *
    * @param callback Callback to execute.
    */
    moduleStart(callback: (details: ModuleStartCallbackObject) => any): any;

    /**
    * Register a callback to fire whenever a test ends.
    *
    * @param callback Callback to execute.
    */
    testDone(callback: (details: TestDoneCallbackObject) => any): any;

    /**
    * Register a callback to fire whenever a test begins.
    *
    * @param callback Callback to execute.
    */
    testStart(callback: (details: TestStartCallbackObject) => any): any;
    
    /* CONFIGURATION */

    /**
    * QUnit has a bunch of internal configuration defaults, some of which are 
    * useful to override. Check the description for each option for details.
    */
    config: Config;
    
    /* TEST */

    /**
    * Add an asynchronous test to run. The test must include a call to start().
    *
    * For testing asynchronous code, asyncTest will automatically stop the test runner 
    * and wait for your code to call start() to continue.
    *
    * @param name Title of unit being tested
    * @param expected Number of assertions in this test
    * @param test Function to close over assertions
    */
    asyncTest(name: string, expected: number, test: (assert: QUnitAssert) => any): any;

    /**
    * Add an asynchronous test to run. The test must include a call to start().
    *
    * For testing asynchronous code, asyncTest will automatically stop the test runner 
    * and wait for your code to call start() to continue.
    *
    * @param name Title of unit being tested
    * @param test Function to close over assertions
    */
    asyncTest(name: string, test: (assert: QUnitAssert) => any): any;

    /**
    * Specify how many assertions are expected to run within a test.
    *
    * To ensure that an explicit number of assertions are run within any test, use 
    * expect( number ) to register an expected count. If the number of assertions 
    * run does not match the expected count, the test will fail.
    *
    * @param amount Number of assertions in this test.
    * @depricated since version 1.16
    */
    expect(amount: number): any;

    /**
    * Group related tests under a single label.
    *
    * All tests that occur after a call to module() will be grouped into that module. 
    * The test names will all be preceded by the module name in the test results. 
    * You can then use that module name to select tests to run.
    *
    * @param name Label for this group of tests
    * @param lifecycle Callbacks to run before and after each test
    */
    module(name: string, lifecycle?: LifecycleObject): any;

    /**
    * Add a test to run.
    *
    * When testing the most common, synchronous code, use test().
    * The assert argument to the callback contains all of QUnit's assertion methods. 
    * If you are avoiding using any of QUnit's globals, you can use the assert 
    * argument instead.
    * 
    * @param title Title of unit being tested
    * @param expected Number of assertions in this test
    * @param test Function to close over assertions
    */
    test(title: string, expected: number, test: (assert: QUnitAssert) => any): any;

    /**
    * @param title Title of unit being tested
    * @param test Function to close over assertions
    */
    test(title: string, test: (assert: QUnitAssert) => any): any;

    /**
    * https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L1568
    */
    equiv(a: any, b: any): any;

    /**
    * https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L897
    */
    push(result: any, actual: any, expected: any, message: string): any;

    /**
    * https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L839
    */
    reset(): any;
}

/* ASSERT */

/**
* A deep recursive comparison assertion, working on primitive types, arrays, objects, 
* regular expressions, dates and functions.
*
* The deepEqual() assertion can be used just like equal() when comparing the value of 
* objects, such that { key: value } is equal to { key: value }. For non-scalar values, 
* identity will be disregarded by deepEqual.
*
* @param actual Object or Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function deepEqual(actual: any, expected: any, message?: string): any;

/** 
* A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
*
* The equal assertion uses the simple comparison operator (==) to compare the actual 
* and expected arguments. When they are equal, the assertion passes: any; otherwise, it fails. 
* When it fails, both actual and expected values are displayed in the test result, 
* in addition to a given message.
* 
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function equal(actual: any, expected: any, message?: string): any;

/**
* An inverted deep recursive comparison assertion, working on primitive types, 
* arrays, objects, regular expressions, dates and functions.
*
* The notDeepEqual() assertion can be used just like equal() when comparing the 
* value of objects, such that { key: value } is equal to { key: value }. For non-scalar 
* values, identity will be disregarded by notDeepEqual.
* 
* @param actual Object or Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function notDeepEqual(actual: any, expected: any, message?: string): any;

/**
* A non-strict comparison assertion, checking for inequality.
*
* The notEqual assertion uses the simple inverted comparison operator (!=) to compare 
* the actual and expected arguments. When they aren't equal, the assertion passes; 
* otherwise, it fails. When it fails, both actual and expected values are displayed 
* in the test result, in addition to a given message.
* 
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function notEqual(actual: any, expected: any, message?: string): any;

/**
* A non-strict comparison assertion, checking for inequality.
*
* The notStrictEqual assertion uses the strict inverted comparison operator (!==) 
* to compare the actual and expected arguments. When they aren't equal, the assertion 
* passes; otherwise, it fails. When it fails, both actual and expected values are 
* displayed in the test result, in addition to a given message.
* 
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function notStrictEqual(actual: any, expected: any, message?: string): any;

/**
* A boolean assertion, equivalent to CommonJS’s assert.ok() and JUnit’s assertTrue(). 
* Passes if the first argument is truthy.
*
* The most basic assertion in QUnit, ok() requires just one argument. If the argument 
* evaluates to true, the assertion passes; otherwise, it fails. If a second message 
* argument is provided, it will be displayed in place of the result.
* 
* @param state Expression being tested
* @param message A short description of the assertion
*/
declare function ok(state: any, message?: string): any;

/**
* A strict type and value comparison assertion.
*
* The strictEqual() assertion provides the most rigid comparison of type and value with 
* the strict equality operator (===)
* 
* @param actual Expression being tested
* @param expected Known comparison value
* @param message A short description of the assertion
*/
declare function strictEqual(actual: any, expected: any, message?: string): any;

/**
* Assertion to test if a callback throws an exception when run.
* 
* When testing code that is expected to throw an exception based on a specific set of 
* circumstances, use throws() to catch the error object for testing and comparison.
* 
* @param block Function to execute
* @param expected Error Object to compare
* @param message A short description of the assertion
*/
declare function throws(block: () => any, expected: any, message?: string): any;

/**
* @param block Function to execute
* @param message A short description of the assertion
*/
declare function throws(block: () => any, message?: string): any;

/* ASYNC CONTROL */

/**
* Start running tests again after the testrunner was stopped. See stop().
*
* When your async test has multiple exit points, call start() for the corresponding number of stop() increments.
* 
* @param decrement Optional argument to merge multiple start() calls into one. Use with multiple corrsponding stop() calls.
*/
declare function start(decrement?: number): any;

/**
* Stop the testrunner to wait for async tests to run. Call start() to continue.
*
* When your async test has multiple exit points, call stop() with the increment argument, corresponding to the number of start() calls you need.
*
* On Blackberry 5.0, window.stop is a native read-only function. If you deal with that browser, use QUnit.stop() instead, which will work anywhere.
*
* @param decrement Optional argument to merge multiple stop() calls into one. Use with multiple corrsponding start() calls.
*/
declare function stop(increment? : number): any;
    
/* CALLBACKS */

/**
* Register a callback to fire whenever the test suite begins.
*
* QUnit.begin() is called once before running any tests. (a better would've been QUnit.start, 
* but thats already in use elsewhere and can't be changed.)
*
* @param callback Callback to execute
*/
declare function begin(callback: () => any): any;

/**
* Register a callback to fire whenever the test suite ends.
*
* @param callback Callback to execute.
*/
declare function done(callback: (details: DoneCallbackObject) => any): any;

/**
* Register a callback to fire whenever an assertion completes.
*
* This is one of several callbacks QUnit provides. Its intended for integration scenarios like 
* PhantomJS or Jenkins. The properties of the details argument are listed below as options.
*
* @param callback Callback to execute.
*/
declare function log(callback: (details: LogCallbackObject) => any): any;

/**
* Register a callback to fire whenever a module ends.
*
* @param callback Callback to execute.
*/
declare function moduleDone(callback: (details: ModuleDoneCallbackObject) => any): any;

/**
* Register a callback to fire whenever a module begins.
*
* @param callback Callback to execute.
*/
declare function moduleStart(callback: (name: string) => any): any;

/**
* Register a callback to fire whenever a test ends.
*
* @param callback Callback to execute.
*/
declare function testDone(callback: (details: TestDoneCallbackObject) => any): any;

/**
* Register a callback to fire whenever a test begins.
*
* @param callback Callback to execute.
*/
declare function testStart(callback: (details: TestStartCallbackObject) => any): any;
    
/* TEST */

/**
* Add an asynchronous test to run. The test must include a call to start().
*
* For testing asynchronous code, asyncTest will automatically stop the test runner 
* and wait for your code to call start() to continue.
*
* @param name Title of unit being tested
* @param expected Number of assertions in this test
* @param test Function to close over assertions
*/
declare function asyncTest(name: string, expected?: any, test?: (assert: QUnitAssert) => any): any;

/**
* Add an asynchronous test to run. The test must include a call to start().
*
* For testing asynchronous code, asyncTest will automatically stop the test runner 
* and wait for your code to call start() to continue.
*
* @param name Title of unit being tested
* @param test Function to close over assertions
*/
declare function asyncTest(name: string, test: (assert: QUnitAssert) => any): any;

/**
* Specify how many assertions are expected to run within a test.
*
* To ensure that an explicit number of assertions are run within any test, use 
* expect( number ) to register an expected count. If the number of assertions 
* run does not match the expected count, the test will fail.
*
* @param amount Number of assertions in this test.
* @depricated since version 1.16
*/
declare function expect(amount: number): any;

// ** conflict with TypeScript module keyword. Must be used on QUnit namespace
//declare var module: (name: string, lifecycle?: LifecycleObject) => any;

/**
* Add a test to run.
*
* When testing the most common, synchronous code, use test().
* The assert argument to the callback contains all of QUnit's assertion methods. 
* If you are avoiding using any of QUnit's globals, you can use the assert 
* argument instead.
* 
* @param title Title of unit being tested
* @param expected Number of assertions in this test
* @param test Function to close over assertions
*/
declare function test(title: string, expected: number, test: (assert?: QUnitAssert) => any): any;

/**
* @param title Title of unit being tested
* @param test Function to close over assertions
*/
declare function test(title: string, test: (assert?: QUnitAssert) => any): any;

declare function notPropEqual(actual: any, expected: any, message?: string): any;

declare function propEqual(actual: any, expected: any, message?: string): any;

// https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L1568
declare function equiv(a: any, b: any): any;

// https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L661
declare var raises: any;

/* QUNIT */
declare var QUnit: QUnitStatic;
