"use strict";

export const spyOn = (object, method) => {

    const original = object[method];
    const mock = {
        calls: [],
        contexts: [],
        results: [],
        get lastCall() { return this.calls[this.calls.length-1]; }
    };

    object[method] = function(...args) {
        const result = { type: 'incomplete' };
        mock.calls.push(args);
        mock.results.push(result);
        mock.contexts.push(this);
        try {
            result.value = original.call(object, args);
            result.type = 'return';
            return result.value;
        } catch (error) {
            result.type = 'throw';
            result.value = error;
            throw error;
        }
    };

    return {
        get called() { return mock.calls.length; },
        mockClear() {
            mock.calls.length = 0;
            mock.contexts.length = 0;
            mock.results.length = 0;
        },
        mockReset() { this.mockClear(); },
        mockRestore() { object[method] = original; },
        get mock() { return mock; }
    };
};

export default spyOn;
