first commit
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Types tests / Test (lts/*) (push) Has been cancelled
				
			
		
			
				
	
				Lint / Lint (lts/*) (push) Has been cancelled
				
			
		
			
				
	
				CodeQL / Analyze (javascript) (push) Has been cancelled
				
			
		
			
				
	
				CI / Test (20) (push) Has been cancelled
				
			
		
			
				
	
				CI / Test (22) (push) Has been cancelled
				
			
		
			
				
	
				CI / Test (24) (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Types tests / Test (lts/*) (push) Has been cancelled
				
			Lint / Lint (lts/*) (push) Has been cancelled
				
			CodeQL / Analyze (javascript) (push) Has been cancelled
				
			CI / Test (20) (push) Has been cancelled
				
			CI / Test (22) (push) Has been cancelled
				
			CI / Test (24) (push) Has been cancelled
				
			This commit is contained in:
		
							
								
								
									
										455
									
								
								test/chromium/test-telemetry.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										455
									
								
								test/chromium/test-telemetry.js
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,455 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
/* Copyright 2016 Mozilla Foundation
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
/* eslint-disable no-var */
 | 
			
		||||
 | 
			
		||||
import assert from "assert";
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import vm from "vm";
 | 
			
		||||
 | 
			
		||||
var SRC_DIR = __dirname + "/../../";
 | 
			
		||||
var telemetryJsPath = "extensions/chromium/telemetry.js";
 | 
			
		||||
var telemetryJsSource = fs.readFileSync(SRC_DIR + telemetryJsPath);
 | 
			
		||||
var telemetryScript = new vm.Script(telemetryJsSource, {
 | 
			
		||||
  filename: telemetryJsPath,
 | 
			
		||||
  displayErrors: true,
 | 
			
		||||
});
 | 
			
		||||
var LOG_URL = /LOG_URL = '([^']+)'/.exec(telemetryJsSource)[1];
 | 
			
		||||
 | 
			
		||||
// Create a minimal extension global that mocks the extension environment that
 | 
			
		||||
// is used by telemetry.js
 | 
			
		||||
function createExtensionGlobal() {
 | 
			
		||||
  var window = {};
 | 
			
		||||
 | 
			
		||||
  // Whenever a "request" was made, the extra headers are appended to this list.
 | 
			
		||||
  var test_requests = (window.test_requests = []);
 | 
			
		||||
 | 
			
		||||
  // Extension API mocks.
 | 
			
		||||
  window.window = window;
 | 
			
		||||
  window.chrome = {};
 | 
			
		||||
  window.chrome.extension = {};
 | 
			
		||||
  window.chrome.extension.inIncognitoContext = false;
 | 
			
		||||
  window.chrome.runtime = {};
 | 
			
		||||
  window.chrome.runtime.id = "oemmndcbldboiebfnladdacbdfmadadm";
 | 
			
		||||
  window.chrome.runtime.getManifest = function () {
 | 
			
		||||
    return { version: "1.0.0" };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  function createStorageAPI() {
 | 
			
		||||
    var storageArea = {};
 | 
			
		||||
    storageArea.get = function (key, callback) {
 | 
			
		||||
      assert.equal(key, "disableTelemetry");
 | 
			
		||||
      // chrome.storage.*. is async, but we make it synchronous to ease testing.
 | 
			
		||||
      callback(storageArea.mock_data);
 | 
			
		||||
    };
 | 
			
		||||
    return storageArea;
 | 
			
		||||
  }
 | 
			
		||||
  window.chrome.storage = {};
 | 
			
		||||
  window.chrome.storage.managed = createStorageAPI();
 | 
			
		||||
  window.chrome.storage.local = createStorageAPI();
 | 
			
		||||
  window.chrome.storage.sync = createStorageAPI();
 | 
			
		||||
 | 
			
		||||
  // Other DOM.
 | 
			
		||||
  window.navigator = {};
 | 
			
		||||
  window.navigator.onLine = true;
 | 
			
		||||
  window.navigator.userAgent =
 | 
			
		||||
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " +
 | 
			
		||||
    "Chrome/50.0.2661.94 Safari/537.36";
 | 
			
		||||
  window.localStorage = {};
 | 
			
		||||
 | 
			
		||||
  var getRandomValues_state = 0;
 | 
			
		||||
  window.crypto = {};
 | 
			
		||||
  window.crypto.getRandomValues = function (buf) {
 | 
			
		||||
    var state = getRandomValues_state++;
 | 
			
		||||
    for (var i = 0; i < buf.length; ++i) {
 | 
			
		||||
      // Totally random byte ;)
 | 
			
		||||
      buf[i] = 0x42 + state;
 | 
			
		||||
    }
 | 
			
		||||
    return buf;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Network-related mocks.
 | 
			
		||||
  window.Request = {};
 | 
			
		||||
  window.Request.prototype = {
 | 
			
		||||
    get mode() {
 | 
			
		||||
      throw new TypeError("Illegal invocation");
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
  window.fetch = function (url, options) {
 | 
			
		||||
    assert.equal(url, LOG_URL);
 | 
			
		||||
    assert.equal(options.method, "POST");
 | 
			
		||||
    assert.equal(options.mode, "cors");
 | 
			
		||||
    assert.ok(!options.body);
 | 
			
		||||
    test_requests.push(options.headers);
 | 
			
		||||
  };
 | 
			
		||||
  window.Headers = function (headers) {
 | 
			
		||||
    headers = JSON.parse(JSON.stringify(headers)); // Clone.
 | 
			
		||||
    Object.keys(headers).forEach(function (k) {
 | 
			
		||||
      headers[k] = String(headers[k]);
 | 
			
		||||
    });
 | 
			
		||||
    return headers;
 | 
			
		||||
  };
 | 
			
		||||
  window.XMLHttpRequest = function () {
 | 
			
		||||
    var invoked = {
 | 
			
		||||
      open: false,
 | 
			
		||||
      send: false,
 | 
			
		||||
    };
 | 
			
		||||
    var headers = {};
 | 
			
		||||
    return {
 | 
			
		||||
      open(method, url) {
 | 
			
		||||
        assert.equal(invoked.open, false);
 | 
			
		||||
        invoked.open = true;
 | 
			
		||||
        assert.equal(method, "POST");
 | 
			
		||||
        assert.equal(url, LOG_URL);
 | 
			
		||||
      },
 | 
			
		||||
      setRequestHeader(k, v) {
 | 
			
		||||
        assert.equal(invoked.open, true);
 | 
			
		||||
        headers[k] = String(v);
 | 
			
		||||
      },
 | 
			
		||||
      send(body) {
 | 
			
		||||
        assert.equal(invoked.open, true);
 | 
			
		||||
        assert.equal(invoked.send, false);
 | 
			
		||||
        invoked.send = true;
 | 
			
		||||
        assert.ok(!body);
 | 
			
		||||
        test_requests.push(headers);
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Time-related logic.
 | 
			
		||||
  var timers = [];
 | 
			
		||||
  window.setInterval = function (callback, ms) {
 | 
			
		||||
    assert.equal(typeof callback, "function");
 | 
			
		||||
    timers.push(callback);
 | 
			
		||||
  };
 | 
			
		||||
  window.Date = {
 | 
			
		||||
    test_now_value: Date.now(),
 | 
			
		||||
    now() {
 | 
			
		||||
      return window.Date.test_now_value;
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
  window.test_fireTimers = function () {
 | 
			
		||||
    assert.ok(timers.length);
 | 
			
		||||
    timers.forEach(function (timer) {
 | 
			
		||||
      timer();
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return window;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Simulate an update of the browser.
 | 
			
		||||
function updateBrowser(window) {
 | 
			
		||||
  window.navigator.userAgent = window.navigator.userAgent.replace(
 | 
			
		||||
    /Chrome\/(\d+)/,
 | 
			
		||||
    function (_, v) {
 | 
			
		||||
      return "Chrome/" + (parseInt(v) + 1);
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tests = [
 | 
			
		||||
  function test_first_run() {
 | 
			
		||||
    // Default settings, run extension for the first time.
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_first_run_incognito() {
 | 
			
		||||
    // The extension should not send any requests when in incognito mode.
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    window.chrome.extension.inIncognitoContext = true;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, []);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_storage_managed_unavailable() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    delete window.chrome.storage.managed;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_managed_pref() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    window.chrome.storage.managed.mock_data = {
 | 
			
		||||
      disableTelemetry: true,
 | 
			
		||||
    };
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, []);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_local_pref() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    window.chrome.storage.local.mock_data = {
 | 
			
		||||
      disableTelemetry: true,
 | 
			
		||||
    };
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, []);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_managed_pref_is_overridden() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    window.chrome.storage.managed.mock_data = {
 | 
			
		||||
      disableTelemetry: true,
 | 
			
		||||
    };
 | 
			
		||||
    window.chrome.storage.sync.mock_data = {
 | 
			
		||||
      disableTelemetry: false,
 | 
			
		||||
    };
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_run_extension_again() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    // Only one request should be sent because of rate-limiting.
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Simulate that quite some hours passed, but it's still rate-limited.
 | 
			
		||||
    window.Date.test_now_value += 11 * 36e5;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    // Only one request should be sent because of rate-limiting.
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Another hour passes and the request should not be rate-limited any more.
 | 
			
		||||
    window.Date.test_now_value += 1 * 36e5;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_running_for_a_while() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Simulate that the timer fired 11 hours since the last ping. The request
 | 
			
		||||
    // should still be rate-limited.
 | 
			
		||||
    window.Date.test_now_value += 11 * 36e5;
 | 
			
		||||
    window.test_fireTimers();
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Another hour passes and the request should not be rate-limited any more.
 | 
			
		||||
    window.Date.test_now_value += 1 * 36e5;
 | 
			
		||||
    window.test_fireTimers();
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_browser_update() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    updateBrowser(window);
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        // Generate a new ID for better privacy.
 | 
			
		||||
        "Deduplication-Id": "4343434343",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_browser_update_between_pref_toggle() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    window.chrome.storage.local.mock_data = {
 | 
			
		||||
      disableTelemetry: true,
 | 
			
		||||
    };
 | 
			
		||||
    updateBrowser(window);
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    window.chrome.storage.local.mock_data = {
 | 
			
		||||
      disableTelemetry: false,
 | 
			
		||||
    };
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        // Generate a new ID for better privacy, even if the update happened
 | 
			
		||||
        // while telemetry was disabled.
 | 
			
		||||
        "Deduplication-Id": "4343434343",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_extension_update() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    window.chrome.runtime.getManifest = function () {
 | 
			
		||||
      return { version: "1.0.1" };
 | 
			
		||||
    };
 | 
			
		||||
    window.Date.test_now_value += 12 * 36e5;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        // The ID did not change because the browser version did not change.
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.1",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_unofficial_build() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    var didWarn = false;
 | 
			
		||||
    window.console = {};
 | 
			
		||||
    window.console.warn = function () {
 | 
			
		||||
      didWarn = true;
 | 
			
		||||
    };
 | 
			
		||||
    window.chrome.runtime.id = "abcdefghijklmnopabcdefghijklmnop";
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, []);
 | 
			
		||||
    assert.ok(didWarn);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_fetch_is_supported() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    // XMLHttpRequest should not be called when fetch is available. So removing
 | 
			
		||||
    // the XMLHttpRequest API should not change behavior.
 | 
			
		||||
    delete window.XMLHttpRequest;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_fetch_not_supported() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    delete window.fetch;
 | 
			
		||||
    delete window.Request;
 | 
			
		||||
    delete window.Headers;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_fetch_mode_not_supported() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    delete window.Request.prototype.mode;
 | 
			
		||||
    window.fetch = function () {
 | 
			
		||||
      throw new Error("Unexpected call to fetch!");
 | 
			
		||||
    };
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  function test_network_offline() {
 | 
			
		||||
    var window = createExtensionGlobal();
 | 
			
		||||
    // Simulate that the network is down for sure.
 | 
			
		||||
    window.navigator.onLine = false;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, []);
 | 
			
		||||
 | 
			
		||||
    // Simulate that the network might be up.
 | 
			
		||||
    window.navigator.onLine = true;
 | 
			
		||||
    telemetryScript.runInNewContext(window);
 | 
			
		||||
    assert.deepEqual(window.test_requests, [
 | 
			
		||||
      {
 | 
			
		||||
        "Deduplication-Id": "4242424242",
 | 
			
		||||
        "Extension-Version": "1.0.0",
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
var test_i = 0;
 | 
			
		||||
 | 
			
		||||
(function next() {
 | 
			
		||||
  var test = tests[test_i++];
 | 
			
		||||
  if (!test) {
 | 
			
		||||
    console.log("All tests completed.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  console.log("Running test " + test_i + "/" + tests.length + ": " + test.name);
 | 
			
		||||
  test();
 | 
			
		||||
  process.nextTick(next);
 | 
			
		||||
})();
 | 
			
		||||
		Reference in New Issue
	
	Block a user