function configureJasmineEnvironment() {
  const timeout = getClientArg('testTimeout');
  if (timeout) {
    jasmine.DEFAULT_TIMEOUT_INTERVAL = Number(timeout);
  }

  const logLevel = getClientArg('logLevel');
  if (logLevel) {
    shaka.log.setLevel(Number(logLevel));
  } else {
    shaka.log.setLevel(shaka.log.Level.INFO);
  }

  beforeAll(async () => {
    // Ensure node modules are loaded before any tests execute.
    await loadNodeModules();

    // Replace jasmine's global error handler, since we have our own more
    // nuanced version.  You can't set this to null, since jasmine still tries
    // to call it.  Note also that our handler uses window.addEventListener
    // instead of window.onerror.
    window.onerror = () => {};
  });

  const originalSetTimeout = window.setTimeout;
  const delayTests = getClientArg('delayTests');
  if (delayTests) {
    afterEach((done) => {  // eslint-disable-line no-restricted-syntax
      console.log('Delaying test by ' + delayTests + ' seconds...');
      originalSetTimeout(done, delayTests * 1000);
    });
  }

  const originalDevice = shaka.device.DeviceFactory.getDevice();
  goog.asserts.assert(originalDevice, 'device must be non-null');
  window.dump(originalDevice.toString());
  window.deviceDetected = originalDevice;

  afterEach(/** @suppress {accessControls} */ () => {
    goog.asserts.assert(originalDevice, 'device must be non-null');
    window.deviceDetected = originalDevice;
    // Reset decoding config cache after each test.
    shaka.util.StreamUtils.clearDecodingConfigCache();
    shaka.media.Capabilities.MediaSourceTypeSupportMap.clear();
  });

  // Code in karma-jasmine's adapter will malform test failures when the
  // expectation message contains a stack trace, losing the failure message and
  // mixing up the stack trace of the failure.  To avoid this, we modify
  // shaka.util.Error not to create a stack trace.  There is never any need for
  // this trace in the tests.
  // Shimming shaka.util.Error proved too complicated because of a combination
  // of compiler restrictions and ES6 language features, so this is by far the
  // simpler answer.
  shaka.util.Error.createStack = false;

  // Shim Jasmine's execute function.  The karma-jasmine adapter will configure
  // jasmine in a way that prevents us from setting our own specFilter config.
  // There is no configuration that will stop karma-jasmine from doing this.
  // So we hook into Jasmine's execute function (the last step of
  // karma-jasmine's startup) to set our own config first.
  // See also https://github.com/karma-runner/karma-jasmine/issues/273
  /** @type {!jasmine.Env} */
  const jasmineEnv = jasmine.getEnv();
  // eslint-disable-next-line no-restricted-syntax
  const originalJasmineExecute = jasmineEnv.execute.bind(jasmineEnv);
  jasmineEnv.execute = () => {
    // Use a RegExp if --filter is set, else empty string will match all.
    const specFilterRegExp = new RegExp(getClientArg('filter') || '');
    const isBrowserSupported = shaka.Player.isBrowserSupported();

    /**
     * A filter over all Jasmine specs.
     * @param {jasmine.Spec} spec
     * @return {boolean}
     */
    function specFilter(spec) {
      // If the browser is not supported, don't run the tests.
      // If the user specified a RegExp, only run the matched tests.
      // Running zero tests is considered an error so the test run will fail on
      // unsupported browsers or if the filter doesn't match any specs.
      return isBrowserSupported && specFilterRegExp.test(spec.getFullName());
    }

    // Set jasmine config.
    const jasmineConfig = {
      specFilter,
      random: !!getClientArg('random'),
      seed: getClientArg('seed'),
    };

    jasmineEnv.configure(jasmineConfig);
    originalJasmineExecute();
  };
}
Apply Jasmine settings and set up anything else needed by the testing
environment.