private _spyOnProperty:()

in packages/jest-jasmine2/src/jasmine/spyRegistry.ts [67:233]


  private _spyOnProperty: (
    obj: Record<string, Spy>,
    propertyName: string,
    accessType: keyof PropertyDescriptor,
  ) => Spy;

  constructor({
    currentSpies = () => [],
  }: {
    currentSpies?: () => Array<Spy>;
  } = {}) {
    this.allowRespy = function (allow) {
      this.respy = allow;
    };

    this.spyOn = (obj, methodName, accessType) => {
      if (accessType) {
        return this._spyOnProperty(obj, methodName, accessType);
      }

      if (obj === void 0) {
        throw new Error(
          getErrorMsg(
            `could not find an object to spy upon for ${methodName}()`,
          ),
        );
      }

      if (methodName === void 0) {
        throw new Error(getErrorMsg('No method name supplied'));
      }

      if (obj[methodName] === void 0) {
        throw new Error(getErrorMsg(`${methodName}() method does not exist`));
      }

      if (obj[methodName] && isSpy(obj[methodName])) {
        if (this.respy) {
          return obj[methodName];
        } else {
          throw new Error(
            getErrorMsg(`${methodName} has already been spied upon`),
          );
        }
      }

      let descriptor;
      try {
        descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
      } catch {
        // IE 8 doesn't support `definePropery` on non-DOM nodes
      }

      if (descriptor && !(descriptor.writable || descriptor.set)) {
        throw new Error(
          getErrorMsg(
            `${methodName} is not declared writable or has no setter`,
          ),
        );
      }

      const originalMethod = obj[methodName];
      const spiedMethod = createSpy(methodName, originalMethod);
      let restoreStrategy;

      if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
        restoreStrategy = function () {
          obj[methodName] = originalMethod;
        };
      } else {
        restoreStrategy = function () {
          if (!delete obj[methodName]) {
            obj[methodName] = originalMethod;
          }
        };
      }

      currentSpies().push({
        restoreObjectToOriginalState: restoreStrategy,
      } as Spy);

      obj[methodName] = spiedMethod;

      return spiedMethod;
    };

    this._spyOnProperty = function (obj, propertyName, accessType = 'get') {
      if (!obj) {
        throw new Error(
          getErrorMsg(
            `could not find an object to spy upon for ${propertyName}`,
          ),
        );
      }

      if (!propertyName) {
        throw new Error(getErrorMsg('No property name supplied'));
      }

      let descriptor: PropertyDescriptor | undefined;
      try {
        descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
      } catch {
        // IE 8 doesn't support `definePropery` on non-DOM nodes
      }

      if (!descriptor) {
        throw new Error(getErrorMsg(`${propertyName} property does not exist`));
      }

      if (!descriptor.configurable) {
        throw new Error(
          getErrorMsg(`${propertyName} is not declared configurable`),
        );
      }

      if (!descriptor[accessType]) {
        throw new Error(
          getErrorMsg(
            `Property ${propertyName} does not have access type ${accessType}`,
          ),
        );
      }

      if (obj[propertyName] && isSpy(obj[propertyName])) {
        if (this.respy) {
          return obj[propertyName];
        } else {
          throw new Error(
            getErrorMsg(`${propertyName} has already been spied upon`),
          );
        }
      }

      const originalDescriptor = descriptor;
      const spiedProperty = createSpy(propertyName, descriptor[accessType]);
      let restoreStrategy;

      if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
        restoreStrategy = function () {
          Object.defineProperty(obj, propertyName, originalDescriptor);
        };
      } else {
        restoreStrategy = function () {
          delete obj[propertyName];
        };
      }

      currentSpies().push({
        restoreObjectToOriginalState: restoreStrategy,
      } as Spy);

      const spiedDescriptor = {...descriptor, [accessType]: spiedProperty};

      Object.defineProperty(obj, propertyName, spiedDescriptor);

      return spiedProperty;
    };

    this.clearSpies = function () {
      const spies = currentSpies();
      for (let i = spies.length - 1; i >= 0; i--) {
        const spyEntry = spies[i];
        spyEntry.restoreObjectToOriginalState!();
      }
    };
  }