describe(‘Spies’, function () {

it('should replace the specified function with a spy object', function() {
  var originalFunctionWasCalled = false;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
    }
  };
  this.spyOn(TestClass, 'someFunction');

  expect(TestClass.someFunction.wasCalled).toEqual(false);
  expect(TestClass.someFunction.callCount).toEqual(0);
  TestClass.someFunction('foo');
  expect(TestClass.someFunction.wasCalled).toEqual(true);
  expect(TestClass.someFunction.callCount).toEqual(1);
  expect(TestClass.someFunction.mostRecentCall.args).toEqual(['foo']);
  expect(TestClass.someFunction.mostRecentCall.object).toEqual(TestClass);
  expect(originalFunctionWasCalled).toEqual(false);

  TestClass.someFunction('bar');
  expect(TestClass.someFunction.callCount).toEqual(2);
  expect(TestClass.someFunction.mostRecentCall.args).toEqual(['bar']);
});

it('should allow you to view args for a particular call', function() {
  var originalFunctionWasCalled = false;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
    }
  };
  this.spyOn(TestClass, 'someFunction');

  TestClass.someFunction('foo');
  TestClass.someFunction('bar');
  expect(TestClass.someFunction.calls[0].args).toEqual(['foo']);
  expect(TestClass.someFunction.calls[1].args).toEqual(['bar']);
  expect(TestClass.someFunction.mostRecentCall.args).toEqual(['bar']);
});

it('should be possible to call through to the original method, or return a specific result', function() {
  var originalFunctionWasCalled = false;
  var passedArgs;
  var passedObj;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
      passedArgs = arguments;
      passedObj = this;
      return "return value from original function";
    }
  };

  this.spyOn(TestClass, 'someFunction').andCallThrough();
  var result = TestClass.someFunction('arg1', 'arg2');
  expect(result).toEqual("return value from original function");
  expect(originalFunctionWasCalled).toEqual(true);
  expect(passedArgs).toEqual(['arg1', 'arg2']);
  expect(passedObj).toEqual(TestClass);
  expect(TestClass.someFunction.wasCalled).toEqual(true);
});

it('should be possible to return a specific value', function() {
  var originalFunctionWasCalled = false;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
      return "return value from original function";
    }
  };

  this.spyOn(TestClass, 'someFunction').andReturn("some value");
  originalFunctionWasCalled = false;
  var result = TestClass.someFunction('arg1', 'arg2');
  expect(result).toEqual("some value");
  expect(originalFunctionWasCalled).toEqual(false);
});

it('should be possible to throw a specific error', function() {
  var originalFunctionWasCalled = false;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
      return "return value from original function";
    }
  };

  this.spyOn(TestClass, 'someFunction').andThrow(new Error('fake error'));
  var exception;
  try {
    TestClass.someFunction('arg1', 'arg2');
  } catch (e) {
    exception = e;
  }
  expect(exception.message).toEqual('fake error');
  expect(originalFunctionWasCalled).toEqual(false);
});

it('should be possible to call a specified function', function() {
  var originalFunctionWasCalled = false;
  var fakeFunctionWasCalled = false;
  var passedArgs;
  var passedObj;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
      return "return value from original function";
    }
  };

  this.spyOn(TestClass, 'someFunction').andCallFake(function() {
    fakeFunctionWasCalled = true;
    passedArgs = arguments;
    passedObj = this;
    return "return value from fake function";
  });

  var result = TestClass.someFunction('arg1', 'arg2');
  expect(result).toEqual("return value from fake function");
  expect(originalFunctionWasCalled).toEqual(false);
  expect(fakeFunctionWasCalled).toEqual(true);
  expect(passedArgs).toEqual(['arg1', 'arg2']);
  expect(passedObj).toEqual(TestClass);
  expect(TestClass.someFunction.wasCalled).toEqual(true);
});

it('is torn down when this.removeAllSpies is called', function() {
  var originalFunctionWasCalled = false;
  var TestClass = {
    someFunction: function() {
      originalFunctionWasCalled = true;
    }
  };
  this.spyOn(TestClass, 'someFunction');

  TestClass.someFunction('foo');
  expect(originalFunctionWasCalled).toEqual(false);

  this.removeAllSpies();

  TestClass.someFunction('foo');
  expect(originalFunctionWasCalled).toEqual(true);
});

it('calls removeAllSpies during spec finish', function() {
  var test = new jasmine.Spec(new jasmine.Env(), {}, 'sample test');

  this.spyOn(test, 'removeAllSpies');

  test.finish();

  expect(test.removeAllSpies).wasCalled();
});

it('throws an exception when some method is spied on twice', function() {
  var TestClass = { someFunction: function() {
  } };
  this.spyOn(TestClass, 'someFunction');
  var exception;
  try {
    this.spyOn(TestClass, 'someFunction');
  } catch (e) {
    exception = e;
  }
  expect(exception).toBeDefined();
});

      it('to spy on an undefined method throws exception', function() {
              var TestClass = {
                      someFunction : function() {
                      }
              };
              function efunc() {
                      this.spyOn(TestClass, 'someOtherFunction');
              };
              expect(function() {
                      efunc();
              }).toThrow('someOtherFunction() method does not exist');

      }); 

it('should be able to reset a spy', function() {
  var TestClass = { someFunction: function() {} };
  this.spyOn(TestClass, 'someFunction');

  expect(TestClass.someFunction).not.toHaveBeenCalled();
  TestClass.someFunction();
  expect(TestClass.someFunction).toHaveBeenCalled();
  TestClass.someFunction.reset();
  expect(TestClass.someFunction).not.toHaveBeenCalled();
  expect(TestClass.someFunction.callCount).toEqual(0);
});

describe("createSpyObj", function() {
  it("should create an object with a bunch of spy methods when you call jasmine.createSpyObj()", function() {
    var spyObj = jasmine.createSpyObj('BaseName', ['method1', 'method2']);
    expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function)});
    expect(spyObj.method1.identity).toEqual('BaseName.method1');
    expect(spyObj.method2.identity).toEqual('BaseName.method2');
  });

  it("should throw if you do not pass an array argument", function() {
    expect(function() {
      jasmine.createSpyObj('BaseName');
    }).toThrow('createSpyObj requires a non-empty array of method names to create spies for');
  });

  it("should throw if you pass an empty array argument", function() {
    expect(function() {
      jasmine.createSpyObj('BaseName');
    }).toThrow('createSpyObj requires a non-empty array of method names to create spies for');
  });
});

});