All files / test TestClass.ts

45.35% Statements 39/86
18.75% Branches 3/16
40% Functions 8/20
45% Lines 36/80

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241              1x       1x           1x                           1x   5x 5x 5x     1x           5x       1x       1x       1x     1x                                                                                                                                                                 32x 32x           32x             32x   32x     32x 32x   32x   32x               32x       1x 32x     32x       1x 32x         32x     32x     32x                           1x                   1x         1x           1x               1x  
import {
  SinonSandbox,
  SinonSpy,
  SinonStub,
  SinonMock,
  SinonSandboxConfig
} from 'sinon';
import * as sinon from 'sinon';
/// <reference path="external/qunit/index.d.ts" />
 
/** Defines a test case */
export class TestCase {
  /** Name to use for the test case */
  public name: string;
 
  /** Test case method */
  public test: () => void;
}
 
/** Defines a test case */
export interface TestCaseAsync {
  /** Name to use for the test case */
  name: string;
 
  /** time to wait after pre before invoking post and calling start() */
  stepDelay: number;
 
  /** async steps */
  steps: Array<() => void>;
}
 
export class TestClass {
  constructor(name?: string) {
    QUnit.module(name);
    this.sandboxConfig.injectInto = null;
    this.sandboxConfig.properties = ['spy', 'stub', 'mock', 'sandbox'];
  }
 
  public static isPollingStepFlag = 'isPollingStep';
 
  /** The instance of the currently running suite. */
  public static currentTestClass: TestClass;
 
  /** Turns on/off sinon's fake implementation of XMLHttpRequest. On by default. */
  public sandboxConfig: SinonSandboxConfig = {};
 
  /** Method called before the start of each test method */
  // tslint:disable-next-line:no-empty
  public testInitialize() {}
 
  /** Method called after each test method has completed */
  // tslint:disable-next-line:no-empty
  public testCleanup() {}
 
  /** Method in which test class intances should call this.testCase(...) to register each of this suite's tests. */
  // tslint:disable-next-line:no-empty
  public registerTests() {}
 
  /** Register an async Javascript unit testcase. */
  public testCaseAsync(testInfo: TestCaseAsync) {
    if (!testInfo.name) {
      throw new Error(
        'Must specify name in testInfo context in registerTestcase call'
      );
    }
 
    if (isNaN(testInfo.stepDelay)) {
      throw new Error('Must specify \'stepDelay\' period between pre and post');
    }
 
    if (!testInfo.steps) {
      throw new Error('Must specify \'steps\' to take asynchronously');
    }
 
    // Create a wrapper around the test method so we can do test initilization and cleanup.
    var testMethod = (assert: any) => {
      var done = assert.async();
 
      // Save off the instance of the currently running suite.
      TestClass.currentTestClass = this;
 
      // Run the test.
      try {
        this._testStarting();
 
        var steps = testInfo.steps;
        var trigger = () => {
          if (steps.length) {
            var step = steps.shift();
 
            // The callback which activates the next test step.
            var nextTestStepTrigger = () => {
              setTimeout(() => {
                trigger();
              }, testInfo.stepDelay);
            };
 
            // There 2 types of test steps - simple and polling.
            // Upon completion of the simple test step the next test step will be called.
            // In case of polling test step the next test step is passed to the polling test step, and
            // it is responsibility of the polling test step to call the next test step.
            try {
              if (step[TestClass.isPollingStepFlag]) {
                step.call(this, nextTestStepTrigger);
              } else {
                step.call(this);
                nextTestStepTrigger.call(this);
              }
            } catch (e) {
              this._testCompleted();
              QUnit.assert.ok(false, e.toString());
 
              // done is QUnit callback indicating the end of the test
              done();
 
              return;
            }
          } else {
            this._testCompleted();
 
            // done is QUnit callback indicating the end of the test
            done();
          }
        };
 
        trigger();
      } catch (ex) {
        QUnit.assert.ok(false, 'Unexpected Exception: ' + ex);
        this._testCompleted(true);
 
        // done is QUnit callback indicating the end of the test
        done();
      }
    };
 
    // Register the test with QUnit
    QUnit.test(testInfo.name, testMethod);
  }
 
  /** Register a Javascript unit testcase. */
  public testCase(testInfo: TestCase) {
    Iif (!testInfo.name) {
      throw new Error(
        'Must specify name in testInfo context in registerTestcase call'
      );
    }
 
    Iif (!testInfo.test) {
      throw new Error(
        'Must specify \'test\' method in testInfo context in registerTestcase call'
      );
    }
 
    // Create a wrapper around the test method so we can do test initilization and cleanup.
    var testMethod = () => {
      // Save off the instance of the currently running suite.
      TestClass.currentTestClass = this;
 
      // Run the test.
      try {
        this._testStarting();
 
        testInfo.test.call(this);
 
        this._testCompleted();
      } catch (ex) {
        QUnit.assert.ok(false, 'Unexpected Exception: ' + ex);
        this._testCompleted(true);
      }
    };
 
    // Register the test with QUnit
    QUnit.test(testInfo.name, testMethod);
  }
 
  /** Called when the test is starting. */
  private _testStarting() {
    this.sandbox = sinon.createSandbox(this.sandboxConfig);
 
    // Allow the derived class to perform test initialization.
    this.testInitialize();
  }
 
  /** Called when the test is completed. */
  private _testCompleted(failed?: boolean) {
    Iif (failed) {
      // Just cleanup the sandbox since the test has already failed.
      this.sandbox.restore();
    } else {
      // Verify the sandbox and restore.
      (<any>this.sandbox).verifyAndRestore();
    }
 
    this.testCleanup();
 
    // Clear the instance of the currently running suite.
    TestClass.currentTestClass = null;
  }
 
  /**** Sinon methods and properties ***/
 
  public sandbox: SinonSandbox;
 
  /** Creates an anonymous function that records arguments, this value, exceptions and return values for all calls. */
  public spy(): SinonSpy;
  /** Spies on the provided function */
  public spy(funcToWrap: Function): SinonSpy;
  /** Creates a spy for object.methodName and replaces the original method with the spy. The spy acts exactly like the original method in all cases.
   *  The original method can be restored by calling object.methodName.restore(). The returned spy is the function object which replaced the original method. spy === object.method. */
  public spy(object: any, methodName: string, func?: Function): SinonSpy;
  public spy(...args: any[]): SinonSpy {
    return null;
  }
 
  /** Creates an anonymous stub function. */
  public stub(): SinonStub;
  /** Stubs all the object's methods. */
  public stub(object: any): SinonStub;
  /** Replaces object.methodName with a func, wrapped in a spy. As usual, object.methodName.restore(); can be used to restore the original method. */
  public stub(object: any, methodName: string, func?: Function): SinonStub;
  public stub(...args: any[]): SinonStub {
    return null;
  }
 
  /** Creates a mock for the provided object.Does not change the object, but returns a mock object to set expectations on the object's methods. */
  public mock(object: any): SinonMock {
    return null;
  }
 
  /**** end: Sinon methods and properties ***/
 
  protected setUserAgent(userAgent: string) {
    Object.defineProperty(window.navigator, 'userAgent', {
      configurable: true,
      get: function() {
        return userAgent;
      }
    });
  }
}