// Type definitions for Sinon 1.16.0
// Project: http://sinonjs.org/
// Definitions by: William Sears <https://github.com/mrbigdog2u>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

// sinon uses DOM dependencies which are absent in browserless envoronment like node.js
// to avoid compiler errors this monkey patch is used
// see more details in https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11351
interface Event { }
interface Document { }

declare namespace Sinon {
    interface SinonSpyCallApi {
        // Properties
        thisValue: any;
        args: any[];
        exception: any;
        returnValue: any;

        // Methods
        calledOn(obj: any): boolean;
        calledWith(...args: any[]): boolean;
        calledWithExactly(...args: any[]): boolean;
        calledWithMatch(...args: any[]): boolean;
        notCalledWith(...args: any[]): boolean;
        notCalledWithMatch(...args: any[]): boolean;
        returned(value: any): boolean;
        threw(): boolean;
        threw(type: string): boolean;
        threw(obj: any): boolean;
        callArg(pos: number): void;
        callArgOn(pos: number, obj: any, ...args: any[]): void;
        callArgWith(pos: number, ...args: any[]): void;
        callArgOnWith(pos: number, obj: any, ...args: any[]): void;
        yield(...args: any[]): void;
        yieldOn(obj: any, ...args: any[]): void;
        yieldTo(property: string, ...args: any[]): void;
        yieldToOn(property: string, obj: any, ...args: any[]): void;
    }

    interface SinonSpyCall extends SinonSpyCallApi {
        calledBefore(call: SinonSpyCall): boolean;
        calledAfter(call: SinonSpyCall): boolean;
        calledWithNew(call: SinonSpyCall): boolean;
    }

    interface SinonSpy extends SinonSpyCallApi {
        // Properties
        callCount: number;
        called: boolean;
        notCalled: boolean;
        calledOnce: boolean;
        calledTwice: boolean;
        calledThrice: boolean;
        firstCall: SinonSpyCall;
        secondCall: SinonSpyCall;
        thirdCall: SinonSpyCall;
        lastCall: SinonSpyCall;
        thisValues: any[];
        args: any[][];
        exceptions: any[];
        returnValues: any[];

        // Methods
        (...args: any[]): any;
        calledBefore(anotherSpy: SinonSpy): boolean;
        calledAfter(anotherSpy: SinonSpy): boolean;
        calledWithNew(): boolean;
        withArgs(...args: any[]): SinonSpy;
        alwaysCalledOn(obj: any): boolean;
        alwaysCalledWith(...args: any[]): boolean;
        alwaysCalledWithExactly(...args: any[]): boolean;
        alwaysCalledWithMatch(...args: any[]): boolean;
        neverCalledWith(...args: any[]): boolean;
        neverCalledWithMatch(...args: any[]): boolean;
        alwaysThrew(): boolean;
        alwaysThrew(type: string): boolean;
        alwaysThrew(obj: any): boolean;
        alwaysReturned(obj: any): boolean;
        invokeCallback(...args: any[]): void;
        getCall(n: number): SinonSpyCall;
        reset(): void;
        printf(format: string, ...args: any[]): string;
        restore(): void;
    }

    interface SinonSpyStatic {
        (): SinonSpy;
        (func: any): SinonSpy;
        (obj: any, method: string): SinonSpy;
    }

    interface SinonStatic {
        spy: SinonSpyStatic;
    }

    interface SinonStub extends SinonSpy {
        resetBehavior(): void;
        returns(obj: any): SinonStub;
        returnsArg(index: number): SinonStub;
        returnsThis(): SinonStub;
        throws(type?: string): SinonStub;
        throws(obj: any): SinonStub;
        callsArg(index: number): SinonStub;
        callThrough(): SinonStub;
        callsArgOn(index: number, context: any): SinonStub;
        callsArgWith(index: number, ...args: any[]): SinonStub;
        callsArgOnWith(index: number, context: any, ...args: any[]): SinonStub;
        callsArgAsync(index: number): SinonStub;
        callsArgOnAsync(index: number, context: any): SinonStub;
        callsArgWithAsync(index: number, ...args: any[]): SinonStub;
        callsArgOnWithAsync(index: number, context: any, ...args: any[]): SinonStub;
        callsFake(func: (...args: any[]) => void): SinonStub;
        onCall(n: number): SinonStub;
        onFirstCall(): SinonStub;
        onSecondCall(): SinonStub;
        onThirdCall(): SinonStub;
        yields(...args: any[]): SinonStub;
        yieldsOn(context: any, ...args: any[]): SinonStub;
        yieldsTo(property: string, ...args: any[]): SinonStub;
        yieldsToOn(property: string, context: any, ...args: any[]): SinonStub;
        yieldsAsync(...args: any[]): SinonStub;
        yieldsOnAsync(context: any, ...args: any[]): SinonStub;
        yieldsToAsync(property: string, ...args: any[]): SinonStub;
        yieldsToOnAsync(property: string, context: any, ...args: any[]): SinonStub;
        withArgs(...args: any[]): SinonStub;
    }

    interface SinonStubStatic {
        (): SinonStub;
        (obj: any): SinonStub;
        (obj: any, method: string): SinonStub;
        (obj: any, method: string, func: any): SinonStub;
    }

    interface SinonStatic {
        stub: SinonStubStatic;
    }

    interface SinonExpectation extends SinonStub {
        atLeast(n: number): SinonExpectation;
        atMost(n: number): SinonExpectation;
        never(): SinonExpectation;
        once(): SinonExpectation;
        twice(): SinonExpectation;
        thrice(): SinonExpectation;
        exactly(n: number): SinonExpectation;
        withArgs(...args: any[]): SinonExpectation;
        withExactArgs(...args: any[]): SinonExpectation;
        on(obj: any): SinonExpectation;
        verify(): SinonExpectation;
        restore(): void;
    }

    interface SinonExpectationStatic {
        create(methodName?: string): SinonExpectation;
    }

    interface SinonMock {
        expects(method: string): SinonExpectation;
        restore(): void;
        verify(): void;
    }

    interface SinonMockStatic {
        (): SinonExpectation;
        (obj: any): SinonMock;
    }

    interface SinonStatic {
        expectation: SinonExpectationStatic;
        mock: SinonMockStatic;
    }

    interface SinonFakeTimers {
        now: number;
        create(now: number): SinonFakeTimers;
        setTimeout(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number;
        clearTimeout(id: number): void;
        setInterval(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number;
        clearInterval(id: number): void;
        tick(ms: number): number;
        reset(): void;
        Date(): Date;
        Date(year: number): Date;
        Date(year: number, month: number): Date;
        Date(year: number, month: number, day: number): Date;
        Date(year: number, month: number, day: number, hour: number): Date;
        Date(year: number, month: number, day: number, hour: number, minute: number): Date;
        Date(year: number, month: number, day: number, hour: number, minute: number, second: number): Date;
        Date(year: number, month: number, day: number, hour: number, minute: number, second: number, ms: number): Date;
        restore(): void;

		/**
		 * Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp
		 * without affecting timers, intervals or immediates.
		 * @param now The new 'now' in unix milliseconds
		 */
		setSystemTime(now: number): void;
		/**
		 * Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp
		 * without affecting timers, intervals or immediates.
		 * @param now The new 'now' as a JavaScript Date
		 */
		setSystemTime(date: Date): void;
	}

    interface SinonFakeTimersStatic {
        (): SinonFakeTimers;
        (...timers: string[]): SinonFakeTimers;
        (now: number, ...timers: string[]): SinonFakeTimers;
    }

    interface SinonStatic {
        useFakeTimers: SinonFakeTimersStatic;
        clock: SinonFakeTimers;
    }

    interface SinonFakeUploadProgress {
        eventListeners: {
            progress: any[];
            load: any[];
            abort: any[];
            error: any[];
        };

        addEventListener(event: string, listener: (e: Event) => any): void;
        removeEventListener(event: string, listener: (e: Event) => any): void;
        dispatchEvent(event: Event): void;
    }

    interface SinonFakeXMLHttpRequest {
        // Properties
        onCreate: (xhr: SinonFakeXMLHttpRequest) => void;
        url: string;
        method: string;
        requestHeaders: any;
        requestBody: string;
        status: number;
        statusText: string;
        async: boolean;
        username: string;
        password: string;
        withCredentials: boolean;
        upload: SinonFakeUploadProgress;
        responseXML: Document;
        getResponseHeader(header: string): string;
        getAllResponseHeaders(): any;

        // Methods
        restore(): void;
        useFilters: boolean;
        addFilter(filter: (method: string, url: string, async: boolean, username: string, password: string) => boolean): void;
        setResponseHeaders(headers: any): void;
        setResponseBody(body: string): void;
        respond(status: number, headers: any, body: string): void;
        autoRespond(ms: number): void;
    }

    interface SinonFakeXMLHttpRequestStatic {
        (): SinonFakeXMLHttpRequest;
    }

    interface SinonStatic {
        useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic;
        FakeXMLHttpRequest: SinonFakeXMLHttpRequest;
    }

    interface SinonFakeServer {
        // Properties
        autoRespond: boolean;
        autoRespondAfter: number;
        fakeHTTPMethods: boolean;
        getHTTPMethod: (request: SinonFakeXMLHttpRequest) => string;
        requests: SinonFakeXMLHttpRequest[];
        respondImmediately: boolean;

        // Methods
        respondWith(body: string): void;
        respondWith(response: any[]): void;
        respondWith(fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
        respondWith(url: string, body: string): void;
        respondWith(url: string, response: any[]): void;
        respondWith(url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
        respondWith(method: string, url: string, body: string): void;
        respondWith(method: string, url: string, response: any[]): void;
        respondWith(method: string, url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
        respondWith(url: RegExp, body: string): void;
        respondWith(url: RegExp, response: any[]): void;
        respondWith(url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
        respondWith(method: string, url: RegExp, body: string): void;
        respondWith(method: string, url: RegExp, response: any[]): void;
        respondWith(method: string, url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void;
        respond(): void;
        restore(): void;
    }

    interface SinonFakeServerStatic {
        create(): SinonFakeServer;
    }

    interface SinonStatic {
        fakeServer: SinonFakeServerStatic;
        fakeServerWithClock: SinonFakeServerStatic;
    }

    interface SinonExposeOptions {
        prefix?: string;
        includeFail?: boolean;
    }

    interface SinonAssert {
        // Properties
        failException: string;
        fail: (message?: string) => void; // Overridable
        pass: (assertion: any) => void; // Overridable

        // Methods
        notCalled(spy: SinonSpy): void;
        called(spy: SinonSpy): void;
        calledOnce(spy: SinonSpy): void;
        calledTwice(spy: SinonSpy): void;
        calledThrice(spy: SinonSpy): void;
        callCount(spy: SinonSpy, count: number): void;
        callOrder(...spies: SinonSpy[]): void;
        calledOn(spy: SinonSpy, obj: any): void;
        alwaysCalledOn(spy: SinonSpy, obj: any): void;
        calledWith(spy: SinonSpy, ...args: any[]): void;
        alwaysCalledWith(spy: SinonSpy, ...args: any[]): void;
        neverCalledWith(spy: SinonSpy, ...args: any[]): void;
        calledWithExactly(spy: SinonSpy, ...args: any[]): void;
        alwaysCalledWithExactly(spy: SinonSpy, ...args: any[]): void;
        calledWithMatch(spy: SinonSpy, ...args: any[]): void;
        alwaysCalledWithMatch(spy: SinonSpy, ...args: any[]): void;
        neverCalledWithMatch(spy: SinonSpy, ...args: any[]): void;
        threw(spy: SinonSpy): void;
        threw(spy: SinonSpy, exception: string): void;
        threw(spy: SinonSpy, exception: any): void;
        alwaysThrew(spy: SinonSpy): void;
        alwaysThrew(spy: SinonSpy, exception: string): void;
        alwaysThrew(spy: SinonSpy, exception: any): void;
        expose(obj: any, options?: SinonExposeOptions): void;
    }

    interface SinonStatic {
        assert: SinonAssert;
    }

    interface SinonMatcher {
        and(expr: SinonMatcher): SinonMatcher;
        or(expr: SinonMatcher): SinonMatcher;
    }

    interface SinonMatch {
        (value: number): SinonMatcher;
        (value: string): SinonMatcher;
        (expr: RegExp): SinonMatcher;
        (obj: any): SinonMatcher;
        (callback: (value: any) => boolean): SinonMatcher;
        any: SinonMatcher;
        defined: SinonMatcher;
        truthy: SinonMatcher;
        falsy: SinonMatcher;
        bool: SinonMatcher;
        number: SinonMatcher;
        string: SinonMatcher;
        object: SinonMatcher;
        func: SinonMatcher;
        array: SinonMatcher;
        regexp: SinonMatcher;
        date: SinonMatcher;
        same(obj: any): SinonMatcher;
        typeOf(type: string): SinonMatcher;
        instanceOf(type: any): SinonMatcher;
        has(property: string, expect?: any): SinonMatcher;
        hasOwn(property: string, expect?: any): SinonMatcher;
    }

    interface SinonStatic {
        match: SinonMatch;
    }

    interface SinonSandboxConfig {
        injectInto?: any;
        properties?: string[];
        useFakeTimers?: any;
        useFakeServer?: any;
    }

    interface SinonSandbox {
        clock: SinonFakeTimers;
        requests: SinonFakeXMLHttpRequest;
        server: SinonFakeServer;
        spy: SinonSpyStatic;
        stub: SinonStubStatic;
        mock: SinonMockStatic;
        useFakeTimers: SinonFakeTimersStatic;
        useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic;
        useFakeServer(): SinonFakeServer;
        restore(): void;
    }

    interface SinonSandboxStatic {
        create(): SinonSandbox;
        create(config: SinonSandboxConfig): SinonSandbox;
    }

    interface SinonStatic {
        sandbox: SinonSandboxStatic;
    }

    interface SinonTestConfig {
        injectIntoThis?: boolean;
        injectInto?: any;
        properties?: string[];
        useFakeTimers?: boolean;
        useFakeServer?: boolean;
    }

    interface SinonTestWrapper extends SinonSandbox {
        (...args: any[]): any;
    }

    interface SinonStatic {
        config: SinonTestConfig;
        test(fn: (...args: any[]) => any): SinonTestWrapper;
        testCase(tests: any): any;
    }

    // Utility overridables
    interface SinonStatic {
        createStubInstance(constructor: any): any;
        format(obj: any): string;
        log(message: string): void;
        restore(object: any): void;
    }
}

declare var Sinon: Sinon.SinonStatic;

export = Sinon;
export as namespace sinon;
