text: /**
   * Copyright 2017 Google Inc. All rights reserved.
   * Modifications copyright (c) Microsoft Corporation.
   *
   * 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.
   */
  
  const Location = require('./Location');
  
  const TestExpectation = {
    Ok: 'ok',
    Fail: 'fail',
  };
  
  function createHook(callback, name) {
    const location = Location.getCallerLocation();
    return { name, body: callback, location };
  }
  
  class Environment {
    constructor(name) {
      this._name = name;
      this._hooks = [];
    }
  
    name() {
      return this._name;
    }
  
    beforeEach(callback) {
      this._hooks.push(createHook(callback, 'beforeEach'));
      return this;
    }
  
    afterEach(callback) {
      this._hooks.push(createHook(callback, 'afterEach'));
      return this;
    }
  
    beforeAll(callback) {
      this._hooks.push(createHook(callback, 'beforeAll'));
      return this;
    }
  
    afterAll(callback) {
      this._hooks.push(createHook(callback, 'afterAll'));
      return this;
    }
  
    globalSetup(callback) {
      this._hooks.push(createHook(callback, 'globalSetup'));
      return this;
    }
  
    globalTeardown(callback) {
      this._hooks.push(createHook(callback, 'globalTeardown'));
      return this;
    }
  
    hooks(name) {
      return this._hooks.filter(hook => !name || hook.name === name);
    }
  
    isEmpty() {
      return !this._hooks.length;
    }
  }
  
  class Test {
    constructor(suite, name, callback, location) {
      this._suite = suite;
      this._name = name;
      this._fullName = (suite.fullName() + ' ' + name).trim();
      this._skipped = false;
      this._expectation = TestExpectation.Ok;
      this._body = callback;
      this._location = location;
      this._timeout = 100000000;
      this._defaultEnvironment = new Environment(this._fullName);
      this._environments = [this._defaultEnvironment];
      this.Expectations = { ...TestExpectation };
    }
  
    suite() {
      return this._suite;
    }
  
    name() {
      return this._name;
    }
  
    fullName() {
      return this._fullName;
    }
  
    location() {
      return this._location;
    }
  
    body() {
      return this._body;
    }
  
    skipped() {
      return this._skipped;
    }
  
    setSkipped(skipped) {
      this._skipped = skipped;
      return this;
    }
  
    timeout() {
      return this._timeout;
    }
  
    setTimeout(timeout) {
      this._timeout = timeout;
      return this;
    }
  
    expectation() {
      return this._expectation;
    }
  
    setExpectation(expectation) {
      this._expectation = expectation;
      return this;
    }
  
    environment() {
      return this._defaultEnvironment;
    }
  
    addEnvironment(environment) {
      this._environments.push(environment);
      return this;
    }
  
    removeEnvironment(environment) {
      const index = this._environments.indexOf(environment);
      if (index === -1)
        throw new Error(`Environment "${environment.name()}" cannot be removed because it was not added to the suite "${this.fullName()}"`);
      this._environments.splice(index, 1);
      return this;
    }
  }
  
  class Suite {
    constructor(parentSuite, name, location) {
      this._parentSuite = parentSuite;
      this._name = name;
      this._fullName = (parentSuite ? parentSuite.fullName() + ' ' + name : name).trim();
      this._location = location;
      this._skipped = false;
      this._expectation = TestExpectation.Ok;
      this._defaultEnvironment = new Environment(this._fullName);
      this._environments = [this._defaultEnvironment];
      this.Expectations = { ...TestExpectation };
    }
  
    parentSuite() {
      return this._parentSuite;
    }
  
    name() {
      return this._name;
    }
  
    fullName() {
      return this._fullName;
    }
  
    skipped() {
      return this._skipped;
    }
  
    setSkipped(skipped) {
      this._skipped = skipped;
      return this;
    }
  
    location() {
      return this._location;
    }
  
    expectation() {
      return this._expectation;
    }
  
    setExpectation(expectation) {
      this._expectation = expectation;
      return this;
    }
  
    environment() {
      return this._defaultEnvironment;
    }
  
    addEnvironment(environment) {
      this._environments.push(environment);
      return this;
    }
  
    removeEnvironment(environment) {
      const index = this._environments.indexOf(environment);
      if (index === -1)
        throw new Error(`Environment "${environment.name()}" cannot be removed because it was not added to the suite "${this.fullName()}"`);
      this._environments.splice(index, 1);
      return this;
    }
  }
  
  module.exports = { TestExpectation, Environment, Test, Suite };
  