Merge branch 'paginate-environments-bundle' into 'master'
Paginate environments bundle Closes #25499 See merge request !9302
This commit is contained in:
		
						commit
						f15340e044
					
				| 
						 | 
					@ -1,223 +1,192 @@
 | 
				
			||||||
/* eslint-disable no-param-reassign, no-new */
 | 
					/* eslint-disable no-param-reassign, no-new */
 | 
				
			||||||
/* global Vue */
 | 
					 | 
				
			||||||
/* global EnvironmentsService */
 | 
					 | 
				
			||||||
/* global Flash */
 | 
					/* global Flash */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Vue = require('vue');
 | 
					const Vue = require('vue');
 | 
				
			||||||
window.Vue.use(require('vue-resource'));
 | 
					Vue.use(require('vue-resource'));
 | 
				
			||||||
require('../services/environments_service');
 | 
					const EnvironmentsService = require('../services/environments_service');
 | 
				
			||||||
require('./environment_item');
 | 
					const EnvironmentTable = require('./environments_table');
 | 
				
			||||||
 | 
					const EnvironmentsStore = require('../stores/environments_store');
 | 
				
			||||||
 | 
					require('../../vue_shared/components/table_pagination');
 | 
				
			||||||
 | 
					require('../../lib/utils/common_utils');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(() => {
 | 
					module.exports = Vue.component('environment-component', {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', {
 | 
					  components: {
 | 
				
			||||||
    props: {
 | 
					    'environment-table': EnvironmentTable,
 | 
				
			||||||
      store: {
 | 
					    'table-pagination': gl.VueGlPagination,
 | 
				
			||||||
        type: Object,
 | 
					  },
 | 
				
			||||||
        required: true,
 | 
					
 | 
				
			||||||
        default: () => ({}),
 | 
					  data() {
 | 
				
			||||||
      },
 | 
					    const environmentsData = document.querySelector('#environments-list-view').dataset;
 | 
				
			||||||
 | 
					    const store = new EnvironmentsStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      store,
 | 
				
			||||||
 | 
					      state: store.state,
 | 
				
			||||||
 | 
					      visibility: 'available',
 | 
				
			||||||
 | 
					      isLoading: false,
 | 
				
			||||||
 | 
					      cssContainerClass: environmentsData.cssClass,
 | 
				
			||||||
 | 
					      endpoint: environmentsData.environmentsDataEndpoint,
 | 
				
			||||||
 | 
					      canCreateDeployment: environmentsData.canCreateDeployment,
 | 
				
			||||||
 | 
					      canReadEnvironment: environmentsData.canReadEnvironment,
 | 
				
			||||||
 | 
					      canCreateEnvironment: environmentsData.canCreateEnvironment,
 | 
				
			||||||
 | 
					      projectEnvironmentsPath: environmentsData.projectEnvironmentsPath,
 | 
				
			||||||
 | 
					      projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
 | 
				
			||||||
 | 
					      newEnvironmentPath: environmentsData.newEnvironmentPath,
 | 
				
			||||||
 | 
					      helpPagePath: environmentsData.helpPagePath,
 | 
				
			||||||
 | 
					      commitIconSvg: environmentsData.commitIconSvg,
 | 
				
			||||||
 | 
					      playIconSvg: environmentsData.playIconSvg,
 | 
				
			||||||
 | 
					      terminalIconSvg: environmentsData.terminalIconSvg,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Pagination Properties,
 | 
				
			||||||
 | 
					      paginationInformation: {},
 | 
				
			||||||
 | 
					      pageNumber: 1,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    scope() {
 | 
				
			||||||
 | 
					      return gl.utils.getParameterByName('scope');
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    components: {
 | 
					    canReadEnvironmentParsed() {
 | 
				
			||||||
      'environment-item': gl.environmentsList.EnvironmentItem,
 | 
					      return gl.utils.convertPermissionToBoolean(this.canReadEnvironment);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    data() {
 | 
					    canCreateDeploymentParsed() {
 | 
				
			||||||
      const environmentsData = document.querySelector('#environments-list-view').dataset;
 | 
					      return gl.utils.convertPermissionToBoolean(this.canCreateDeployment);
 | 
				
			||||||
 | 
					 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
        state: this.store.state,
 | 
					 | 
				
			||||||
        visibility: 'available',
 | 
					 | 
				
			||||||
        isLoading: false,
 | 
					 | 
				
			||||||
        cssContainerClass: environmentsData.cssClass,
 | 
					 | 
				
			||||||
        endpoint: environmentsData.environmentsDataEndpoint,
 | 
					 | 
				
			||||||
        canCreateDeployment: environmentsData.canCreateDeployment,
 | 
					 | 
				
			||||||
        canReadEnvironment: environmentsData.canReadEnvironment,
 | 
					 | 
				
			||||||
        canCreateEnvironment: environmentsData.canCreateEnvironment,
 | 
					 | 
				
			||||||
        projectEnvironmentsPath: environmentsData.projectEnvironmentsPath,
 | 
					 | 
				
			||||||
        projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
 | 
					 | 
				
			||||||
        newEnvironmentPath: environmentsData.newEnvironmentPath,
 | 
					 | 
				
			||||||
        helpPagePath: environmentsData.helpPagePath,
 | 
					 | 
				
			||||||
        commitIconSvg: environmentsData.commitIconSvg,
 | 
					 | 
				
			||||||
        playIconSvg: environmentsData.playIconSvg,
 | 
					 | 
				
			||||||
        terminalIconSvg: environmentsData.terminalIconSvg,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    computed: {
 | 
					    canCreateEnvironmentParsed() {
 | 
				
			||||||
      scope() {
 | 
					      return gl.utils.convertPermissionToBoolean(this.canCreateEnvironment);
 | 
				
			||||||
        return this.$options.getQueryParameter('scope');
 | 
					    },
 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      canReadEnvironmentParsed() {
 | 
					  },
 | 
				
			||||||
        return this.$options.convertPermissionToBoolean(this.canReadEnvironment);
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      canCreateDeploymentParsed() {
 | 
					  /**
 | 
				
			||||||
        return this.$options.convertPermissionToBoolean(this.canCreateDeployment);
 | 
					   * Fetches all the environments and stores them.
 | 
				
			||||||
      },
 | 
					   * Toggles loading property.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  created() {
 | 
				
			||||||
 | 
					    const scope = gl.utils.getParameterByName('scope') || this.visibility;
 | 
				
			||||||
 | 
					    const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      canCreateEnvironmentParsed() {
 | 
					    const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`;
 | 
				
			||||||
        return this.$options.convertPermissionToBoolean(this.canCreateEnvironment);
 | 
					
 | 
				
			||||||
      },
 | 
					    const service = new EnvironmentsService(endpoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.isLoading = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return service.all()
 | 
				
			||||||
 | 
					      .then(resp => ({
 | 
				
			||||||
 | 
					        headers: resp.headers,
 | 
				
			||||||
 | 
					        body: resp.json(),
 | 
				
			||||||
 | 
					      }))
 | 
				
			||||||
 | 
					      .then((response) => {
 | 
				
			||||||
 | 
					        this.store.storeAvailableCount(response.body.available_count);
 | 
				
			||||||
 | 
					        this.store.storeStoppedCount(response.body.stopped_count);
 | 
				
			||||||
 | 
					        this.store.storeEnvironments(response.body.environments);
 | 
				
			||||||
 | 
					        this.store.setPagination(response.headers);
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .then(() => {
 | 
				
			||||||
 | 
					        this.isLoading = false;
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch(() => {
 | 
				
			||||||
 | 
					        this.isLoading = false;
 | 
				
			||||||
 | 
					        new Flash('An error occurred while fetching the environments.', 'alert');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    toggleRow(model) {
 | 
				
			||||||
 | 
					      return this.store.toggleFolder(model.name);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Fetches all the environments and stores them.
 | 
					     * Will change the page number and update the URL.
 | 
				
			||||||
     * Toggles loading property.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    created() {
 | 
					 | 
				
			||||||
      gl.environmentsService = new EnvironmentsService(this.endpoint);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const scope = this.$options.getQueryParameter('scope');
 | 
					 | 
				
			||||||
      if (scope) {
 | 
					 | 
				
			||||||
        this.store.storeVisibility(scope);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      this.isLoading = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return gl.environmentsService.all()
 | 
					 | 
				
			||||||
        .then(resp => resp.json())
 | 
					 | 
				
			||||||
        .then((json) => {
 | 
					 | 
				
			||||||
          this.store.storeEnvironments(json);
 | 
					 | 
				
			||||||
          this.isLoading = false;
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .catch(() => {
 | 
					 | 
				
			||||||
          this.isLoading = false;
 | 
					 | 
				
			||||||
          new Flash('An error occurred while fetching the environments.', 'alert');
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Transforms the url parameter into an object and
 | 
					 | 
				
			||||||
     * returns the one requested.
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param  {String} param
 | 
					     * @param  {Number} pageNumber desired page to go to.
 | 
				
			||||||
     * @returns {String}       The value of the requested parameter.
 | 
					     * @return {String}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getQueryParameter(parameter) {
 | 
					    changePage(pageNumber) {
 | 
				
			||||||
      return window.location.search.substring(1).split('&').reduce((acc, param) => {
 | 
					      const param = gl.utils.setParamInURL('page', pageNumber);
 | 
				
			||||||
        const paramSplited = param.split('=');
 | 
					 | 
				
			||||||
        acc[paramSplited[0]] = paramSplited[1];
 | 
					 | 
				
			||||||
        return acc;
 | 
					 | 
				
			||||||
      }, {})[parameter];
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					      gl.utils.visitUrl(param);
 | 
				
			||||||
     * Converts permission provided as strings to booleans.
 | 
					      return param;
 | 
				
			||||||
     * @param  {String} string
 | 
					 | 
				
			||||||
     * @returns {Boolean}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    convertPermissionToBoolean(string) {
 | 
					 | 
				
			||||||
      return string === 'true';
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    methods: {
 | 
					  template: `
 | 
				
			||||||
      toggleRow(model) {
 | 
					    <div :class="cssContainerClass">
 | 
				
			||||||
        return this.store.toggleFolder(model.name);
 | 
					      <div class="top-area">
 | 
				
			||||||
      },
 | 
					        <ul v-if="!isLoading" class="nav-links">
 | 
				
			||||||
    },
 | 
					          <li v-bind:class="{ 'active': scope === null || scope === 'available' }">
 | 
				
			||||||
 | 
					            <a :href="projectEnvironmentsPath">
 | 
				
			||||||
    template: `
 | 
					              Available
 | 
				
			||||||
      <div :class="cssContainerClass">
 | 
					              <span class="badge js-available-environments-count">
 | 
				
			||||||
        <div class="top-area">
 | 
					                {{state.availableCounter}}
 | 
				
			||||||
          <ul v-if="!isLoading" class="nav-links">
 | 
					              </span>
 | 
				
			||||||
            <li v-bind:class="{ 'active': scope === undefined }">
 | 
					 | 
				
			||||||
              <a :href="projectEnvironmentsPath">
 | 
					 | 
				
			||||||
                Available
 | 
					 | 
				
			||||||
                <span class="badge js-available-environments-count">
 | 
					 | 
				
			||||||
                  {{state.availableCounter}}
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              </a>
 | 
					 | 
				
			||||||
            </li><li v-bind:class="{ 'active' : scope === 'stopped' }">
 | 
					 | 
				
			||||||
              <a :href="projectStoppedEnvironmentsPath">
 | 
					 | 
				
			||||||
                Stopped
 | 
					 | 
				
			||||||
                <span class="badge js-stopped-environments-count">
 | 
					 | 
				
			||||||
                  {{state.stoppedCounter}}
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              </a>
 | 
					 | 
				
			||||||
            </li>
 | 
					 | 
				
			||||||
          </ul>
 | 
					 | 
				
			||||||
          <div v-if="canCreateEnvironmentParsed && !isLoading" class="nav-controls">
 | 
					 | 
				
			||||||
            <a :href="newEnvironmentPath" class="btn btn-create">
 | 
					 | 
				
			||||||
              New environment
 | 
					 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
          </div>
 | 
					          </li>
 | 
				
			||||||
        </div>
 | 
					          <li v-bind:class="{ 'active' : scope === 'stopped' }">
 | 
				
			||||||
 | 
					            <a :href="projectStoppedEnvironmentsPath">
 | 
				
			||||||
        <div class="environments-container">
 | 
					              Stopped
 | 
				
			||||||
          <div class="environments-list-loading text-center" v-if="isLoading">
 | 
					              <span class="badge js-stopped-environments-count">
 | 
				
			||||||
            <i class="fa fa-spinner fa-spin"></i>
 | 
					                {{state.stoppedCounter}}
 | 
				
			||||||
          </div>
 | 
					              </span>
 | 
				
			||||||
 | 
					 | 
				
			||||||
          <div class="blank-state blank-state-no-icon"
 | 
					 | 
				
			||||||
            v-if="!isLoading && state.environments.length === 0">
 | 
					 | 
				
			||||||
            <h2 class="blank-state-title js-blank-state-title">
 | 
					 | 
				
			||||||
              You don't have any environments right now.
 | 
					 | 
				
			||||||
            </h2>
 | 
					 | 
				
			||||||
            <p class="blank-state-text">
 | 
					 | 
				
			||||||
              Environments are places where code gets deployed, such as staging or production.
 | 
					 | 
				
			||||||
              <br />
 | 
					 | 
				
			||||||
              <a :href="helpPagePath">
 | 
					 | 
				
			||||||
                Read more about environments
 | 
					 | 
				
			||||||
              </a>
 | 
					 | 
				
			||||||
            </p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <a
 | 
					 | 
				
			||||||
              v-if="canCreateEnvironmentParsed"
 | 
					 | 
				
			||||||
              :href="newEnvironmentPath"
 | 
					 | 
				
			||||||
              class="btn btn-create js-new-environment-button">
 | 
					 | 
				
			||||||
              New Environment
 | 
					 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
          </div>
 | 
					          </li>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
          <div class="table-holder"
 | 
					        <div v-if="canCreateEnvironmentParsed && !isLoading" class="nav-controls">
 | 
				
			||||||
            v-if="!isLoading && state.filteredEnvironments.length > 0">
 | 
					          <a :href="newEnvironmentPath" class="btn btn-create">
 | 
				
			||||||
            <table class="table ci-table environments">
 | 
					            New environment
 | 
				
			||||||
              <thead>
 | 
					          </a>
 | 
				
			||||||
                <tr>
 | 
					 | 
				
			||||||
                  <th class="environments-name">Environment</th>
 | 
					 | 
				
			||||||
                  <th class="environments-deploy">Last deployment</th>
 | 
					 | 
				
			||||||
                  <th class="environments-build">Job</th>
 | 
					 | 
				
			||||||
                  <th class="environments-commit">Commit</th>
 | 
					 | 
				
			||||||
                  <th class="environments-date">Updated</th>
 | 
					 | 
				
			||||||
                  <th class="hidden-xs environments-actions"></th>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
              </thead>
 | 
					 | 
				
			||||||
              <tbody>
 | 
					 | 
				
			||||||
                <template v-for="model in state.filteredEnvironments"
 | 
					 | 
				
			||||||
                  v-bind:model="model">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  <tr
 | 
					 | 
				
			||||||
                    is="environment-item"
 | 
					 | 
				
			||||||
                    :model="model"
 | 
					 | 
				
			||||||
                    :toggleRow="toggleRow.bind(model)"
 | 
					 | 
				
			||||||
                    :can-create-deployment="canCreateDeploymentParsed"
 | 
					 | 
				
			||||||
                    :can-read-environment="canReadEnvironmentParsed"
 | 
					 | 
				
			||||||
                    :play-icon-svg="playIconSvg"
 | 
					 | 
				
			||||||
                    :terminal-icon-svg="terminalIconSvg"
 | 
					 | 
				
			||||||
                    :commit-icon-svg="commitIconSvg"></tr>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  <tr v-if="model.isOpen && model.children && model.children.length > 0"
 | 
					 | 
				
			||||||
                    is="environment-item"
 | 
					 | 
				
			||||||
                    v-for="children in model.children"
 | 
					 | 
				
			||||||
                    :model="children"
 | 
					 | 
				
			||||||
                    :toggleRow="toggleRow.bind(children)"
 | 
					 | 
				
			||||||
                    :can-create-deployment="canCreateDeploymentParsed"
 | 
					 | 
				
			||||||
                    :can-read-environment="canReadEnvironmentParsed"
 | 
					 | 
				
			||||||
                    :play-icon-svg="playIconSvg"
 | 
					 | 
				
			||||||
                    :terminal-icon-svg="terminalIconSvg"
 | 
					 | 
				
			||||||
                    :commit-icon-svg="commitIconSvg">
 | 
					 | 
				
			||||||
                    </tr>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                </template>
 | 
					 | 
				
			||||||
              </tbody>
 | 
					 | 
				
			||||||
            </table>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    `,
 | 
					
 | 
				
			||||||
  });
 | 
					      <div class="environments-container">
 | 
				
			||||||
})();
 | 
					        <div class="environments-list-loading text-center" v-if="isLoading">
 | 
				
			||||||
 | 
					          <i class="fa fa-spinner fa-spin"></i>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="blank-state blank-state-no-icon"
 | 
				
			||||||
 | 
					          v-if="!isLoading && state.environments.length === 0">
 | 
				
			||||||
 | 
					          <h2 class="blank-state-title js-blank-state-title">
 | 
				
			||||||
 | 
					            You don't have any environments right now.
 | 
				
			||||||
 | 
					          </h2>
 | 
				
			||||||
 | 
					          <p class="blank-state-text">
 | 
				
			||||||
 | 
					            Environments are places where code gets deployed, such as staging or production.
 | 
				
			||||||
 | 
					            <br />
 | 
				
			||||||
 | 
					            <a :href="helpPagePath">
 | 
				
			||||||
 | 
					              Read more about environments
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <a v-if="canCreateEnvironmentParsed"
 | 
				
			||||||
 | 
					            :href="newEnvironmentPath"
 | 
				
			||||||
 | 
					            class="btn btn-create js-new-environment-button">
 | 
				
			||||||
 | 
					            New Environment
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="table-holder"
 | 
				
			||||||
 | 
					          v-if="!isLoading && state.environments.length > 0">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <environment-table
 | 
				
			||||||
 | 
					            :environments="state.environments"
 | 
				
			||||||
 | 
					            :can-create-deployment="canCreateDeploymentParsed"
 | 
				
			||||||
 | 
					            :can-read-environment="canReadEnvironmentParsed"
 | 
				
			||||||
 | 
					            :play-icon-svg="playIconSvg"
 | 
				
			||||||
 | 
					            :terminal-icon-svg="terminalIconSvg"
 | 
				
			||||||
 | 
					            :commit-icon-svg="commitIconSvg">
 | 
				
			||||||
 | 
					          </environment-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
 | 
				
			||||||
 | 
					            :change="changePage"
 | 
				
			||||||
 | 
					            :pageInfo="state.paginationInformation">
 | 
				
			||||||
 | 
					          </table-pagination>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  `,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,50 +1,43 @@
 | 
				
			||||||
/* global Vue */
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Vue = require('vue');
 | 
					module.exports = Vue.component('actions-component', {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
(() => {
 | 
					    actions: {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					      type: Array,
 | 
				
			||||||
  window.gl.environmentsList = window.gl.environmentsList || {};
 | 
					      required: false,
 | 
				
			||||||
 | 
					      default: () => [],
 | 
				
			||||||
  gl.environmentsList.ActionsComponent = Vue.component('actions-component', {
 | 
					 | 
				
			||||||
    props: {
 | 
					 | 
				
			||||||
      actions: {
 | 
					 | 
				
			||||||
        type: Array,
 | 
					 | 
				
			||||||
        required: false,
 | 
					 | 
				
			||||||
        default: () => [],
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      playIconSvg: {
 | 
					 | 
				
			||||||
        type: String,
 | 
					 | 
				
			||||||
        required: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template: `
 | 
					    playIconSvg: {
 | 
				
			||||||
      <div class="inline">
 | 
					      type: String,
 | 
				
			||||||
        <div class="dropdown">
 | 
					      required: false,
 | 
				
			||||||
          <a class="dropdown-new btn btn-default" data-toggle="dropdown">
 | 
					    },
 | 
				
			||||||
            <span class="js-dropdown-play-icon-container" v-html="playIconSvg"></span>
 | 
					  },
 | 
				
			||||||
            <i class="fa fa-caret-down"></i>
 | 
					 | 
				
			||||||
          </a>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ul class="dropdown-menu dropdown-menu-align-right">
 | 
					  template: `
 | 
				
			||||||
            <li v-for="action in actions">
 | 
					    <div class="inline">
 | 
				
			||||||
              <a :href="action.play_path"
 | 
					      <div class="dropdown">
 | 
				
			||||||
                data-method="post"
 | 
					        <a class="dropdown-new btn btn-default" data-toggle="dropdown">
 | 
				
			||||||
                rel="nofollow"
 | 
					          <span class="js-dropdown-play-icon-container" v-html="playIconSvg"></span>
 | 
				
			||||||
                class="js-manual-action-link">
 | 
					          <i class="fa fa-caret-down"></i>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <span class="js-action-play-icon-container" v-html="playIconSvg"></span>
 | 
					        <ul class="dropdown-menu dropdown-menu-align-right">
 | 
				
			||||||
 | 
					          <li v-for="action in actions">
 | 
				
			||||||
 | 
					            <a :href="action.play_path"
 | 
				
			||||||
 | 
					              data-method="post"
 | 
				
			||||||
 | 
					              rel="nofollow"
 | 
				
			||||||
 | 
					              class="js-manual-action-link">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <span>
 | 
					              <span class="js-action-play-icon-container" v-html="playIconSvg"></span>
 | 
				
			||||||
                  {{action.name}}
 | 
					
 | 
				
			||||||
                </span>
 | 
					              <span>
 | 
				
			||||||
              </a>
 | 
					                {{action.name}}
 | 
				
			||||||
            </li>
 | 
					              </span>
 | 
				
			||||||
          </ul>
 | 
					            </a>
 | 
				
			||||||
        </div>
 | 
					          </li>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    `,
 | 
					    </div>
 | 
				
			||||||
  });
 | 
					  `,
 | 
				
			||||||
})();
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,19 @@
 | 
				
			||||||
/* global Vue */
 | 
					/**
 | 
				
			||||||
 | 
					 * Renders the external url link in environments table.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Vue = require('vue');
 | 
					module.exports = Vue.component('external-url-component', {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
(() => {
 | 
					    externalUrl: {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					      type: String,
 | 
				
			||||||
  window.gl.environmentsList = window.gl.environmentsList || {};
 | 
					      default: '',
 | 
				
			||||||
 | 
					 | 
				
			||||||
  gl.environmentsList.ExternalUrlComponent = Vue.component('external-url-component', {
 | 
					 | 
				
			||||||
    props: {
 | 
					 | 
				
			||||||
      externalUrl: {
 | 
					 | 
				
			||||||
        type: String,
 | 
					 | 
				
			||||||
        default: '',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template: `
 | 
					  template: `
 | 
				
			||||||
      <a class="btn external_url" :href="externalUrl" target="_blank">
 | 
					    <a class="btn external_url" :href="externalUrl" target="_blank">
 | 
				
			||||||
        <i class="fa fa-external-link"></i>
 | 
					      <i class="fa fa-external-link"></i>
 | 
				
			||||||
      </a>
 | 
					    </a>
 | 
				
			||||||
    `,
 | 
					  `,
 | 
				
			||||||
  });
 | 
					});
 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -1,33 +1,30 @@
 | 
				
			||||||
/* global Vue */
 | 
					/**
 | 
				
			||||||
 | 
					 * Renders Rollback or Re deploy button in environments table depending
 | 
				
			||||||
 | 
					 * of the provided property `isLastDeployment`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Vue = require('vue');
 | 
					module.exports = Vue.component('rollback-component', {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
(() => {
 | 
					    retryUrl: {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					      type: String,
 | 
				
			||||||
  window.gl.environmentsList = window.gl.environmentsList || {};
 | 
					      default: '',
 | 
				
			||||||
 | 
					 | 
				
			||||||
  gl.environmentsList.RollbackComponent = Vue.component('rollback-component', {
 | 
					 | 
				
			||||||
    props: {
 | 
					 | 
				
			||||||
      retryUrl: {
 | 
					 | 
				
			||||||
        type: String,
 | 
					 | 
				
			||||||
        default: '',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      isLastDeployment: {
 | 
					 | 
				
			||||||
        type: Boolean,
 | 
					 | 
				
			||||||
        default: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template: `
 | 
					    isLastDeployment: {
 | 
				
			||||||
      <a class="btn" :href="retryUrl" data-method="post" rel="nofollow">
 | 
					      type: Boolean,
 | 
				
			||||||
        <span v-if="isLastDeployment">
 | 
					      default: true,
 | 
				
			||||||
          Re-deploy
 | 
					    },
 | 
				
			||||||
        </span>
 | 
					  },
 | 
				
			||||||
        <span v-else>
 | 
					
 | 
				
			||||||
          Rollback
 | 
					  template: `
 | 
				
			||||||
        </span>
 | 
					    <a class="btn" :href="retryUrl" data-method="post" rel="nofollow">
 | 
				
			||||||
      </a>
 | 
					      <span v-if="isLastDeployment">
 | 
				
			||||||
    `,
 | 
					        Re-deploy
 | 
				
			||||||
  });
 | 
					      </span>
 | 
				
			||||||
})();
 | 
					      <span v-else>
 | 
				
			||||||
 | 
					        Rollback
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					  `,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,27 +1,24 @@
 | 
				
			||||||
/* global Vue */
 | 
					/**
 | 
				
			||||||
 | 
					 * Renders the stop "button" that allows stop an environment.
 | 
				
			||||||
 | 
					 * Used in environments table.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Vue = require('vue');
 | 
					module.exports = Vue.component('stop-component', {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
(() => {
 | 
					    stopUrl: {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					      type: String,
 | 
				
			||||||
  window.gl.environmentsList = window.gl.environmentsList || {};
 | 
					      default: '',
 | 
				
			||||||
 | 
					 | 
				
			||||||
  gl.environmentsList.StopComponent = Vue.component('stop-component', {
 | 
					 | 
				
			||||||
    props: {
 | 
					 | 
				
			||||||
      stopUrl: {
 | 
					 | 
				
			||||||
        type: String,
 | 
					 | 
				
			||||||
        default: '',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template: `
 | 
					  template: `
 | 
				
			||||||
      <a class="btn stop-env-link"
 | 
					    <a class="btn stop-env-link"
 | 
				
			||||||
        :href="stopUrl"
 | 
					      :href="stopUrl"
 | 
				
			||||||
        data-confirm="Are you sure you want to stop this environment?"
 | 
					      data-confirm="Are you sure you want to stop this environment?"
 | 
				
			||||||
        data-method="post"
 | 
					      data-method="post"
 | 
				
			||||||
        rel="nofollow">
 | 
					      rel="nofollow">
 | 
				
			||||||
        <i class="fa fa-stop stop-env-icon"></i>
 | 
					      <i class="fa fa-stop stop-env-icon" aria-hidden="true"></i>
 | 
				
			||||||
      </a>
 | 
					    </a>
 | 
				
			||||||
    `,
 | 
					  `,
 | 
				
			||||||
  });
 | 
					});
 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,28 +1,25 @@
 | 
				
			||||||
/* global Vue */
 | 
					/**
 | 
				
			||||||
 | 
					 * Renders a terminal button to open a web terminal.
 | 
				
			||||||
 | 
					 * Used in environments table.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Vue = require('vue');
 | 
					module.exports = Vue.component('terminal-button-component', {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
(() => {
 | 
					    terminalPath: {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					      type: String,
 | 
				
			||||||
  window.gl.environmentsList = window.gl.environmentsList || {};
 | 
					      default: '',
 | 
				
			||||||
 | 
					 | 
				
			||||||
  gl.environmentsList.TerminalButtonComponent = Vue.component('terminal-button-component', {
 | 
					 | 
				
			||||||
    props: {
 | 
					 | 
				
			||||||
      terminalPath: {
 | 
					 | 
				
			||||||
        type: String,
 | 
					 | 
				
			||||||
        default: '',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      terminalIconSvg: {
 | 
					 | 
				
			||||||
        type: String,
 | 
					 | 
				
			||||||
        default: '',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    terminalIconSvg: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      default: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template: `
 | 
					  template: `
 | 
				
			||||||
      <a class="btn terminal-button"
 | 
					    <a class="btn terminal-button"
 | 
				
			||||||
        :href="terminalPath">
 | 
					      :href="terminalPath">
 | 
				
			||||||
        <span class="js-terminal-icon-container" v-html="terminalIconSvg"></span>
 | 
					      <span class="js-terminal-icon-container" v-html="terminalIconSvg"></span>
 | 
				
			||||||
      </a>
 | 
					    </a>
 | 
				
			||||||
    `,
 | 
					  `,
 | 
				
			||||||
  });
 | 
					});
 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,74 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Render environments table.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					const EnvironmentItem = require('./environment_item');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = Vue.component('environment-table-component', {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  components: {
 | 
				
			||||||
 | 
					    'environment-item': EnvironmentItem,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    environments: {
 | 
				
			||||||
 | 
					      type: Array,
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					      default: () => ([]),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canReadEnvironment: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      required: false,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canCreateDeployment: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      required: false,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    commitIconSvg: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      required: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    playIconSvg: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      required: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    terminalIconSvg: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      required: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  template: `
 | 
				
			||||||
 | 
					    <table class="table ci-table environments">
 | 
				
			||||||
 | 
					      <thead>
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					          <th class="environments-name">Environment</th>
 | 
				
			||||||
 | 
					          <th class="environments-deploy">Last deployment</th>
 | 
				
			||||||
 | 
					          <th class="environments-build">Job</th>
 | 
				
			||||||
 | 
					          <th class="environments-commit">Commit</th>
 | 
				
			||||||
 | 
					          <th class="environments-date">Updated</th>
 | 
				
			||||||
 | 
					          <th class="hidden-xs environments-actions"></th>
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					      </thead>
 | 
				
			||||||
 | 
					      <tbody>
 | 
				
			||||||
 | 
					        <template v-for="model in environments"
 | 
				
			||||||
 | 
					          v-bind:model="model">
 | 
				
			||||||
 | 
					          <tr is="environment-item"
 | 
				
			||||||
 | 
					            :model="model"
 | 
				
			||||||
 | 
					            :can-create-deployment="canCreateDeployment"
 | 
				
			||||||
 | 
					            :can-read-environment="canReadEnvironment"
 | 
				
			||||||
 | 
					            :play-icon-svg="playIconSvg"
 | 
				
			||||||
 | 
					            :terminal-icon-svg="terminalIconSvg"
 | 
				
			||||||
 | 
					            :commit-icon-svg="commitIconSvg"></tr>
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					      </tbody>
 | 
				
			||||||
 | 
					    </table>
 | 
				
			||||||
 | 
					  `,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
window.Vue = require('vue');
 | 
					const EnvironmentsComponent = require('./components/environment');
 | 
				
			||||||
require('./stores/environments_store');
 | 
					 | 
				
			||||||
require('./components/environment');
 | 
					 | 
				
			||||||
require('../vue_shared/vue_resource_interceptor');
 | 
					require('../vue_shared/vue_resource_interceptor');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(() => {
 | 
					$(() => {
 | 
				
			||||||
| 
						 | 
					@ -9,14 +7,8 @@ $(() => {
 | 
				
			||||||
  if (gl.EnvironmentsListApp) {
 | 
					  if (gl.EnvironmentsListApp) {
 | 
				
			||||||
    gl.EnvironmentsListApp.$destroy(true);
 | 
					    gl.EnvironmentsListApp.$destroy(true);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const Store = gl.environmentsList.EnvironmentsStore;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gl.EnvironmentsListApp = new gl.environmentsList.EnvironmentsComponent({
 | 
					  gl.EnvironmentsListApp = new EnvironmentsComponent({
 | 
				
			||||||
    el: document.querySelector('#environments-list-view'),
 | 
					    el: document.querySelector('#environments-list-view'),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    propsData: {
 | 
					 | 
				
			||||||
      store: Store.create(),
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					const EnvironmentsFolderComponent = require('./environments_folder_view');
 | 
				
			||||||
 | 
					require('../../vue_shared/vue_resource_interceptor');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(() => {
 | 
				
			||||||
 | 
					  window.gl = window.gl || {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (gl.EnvironmentsListFolderApp) {
 | 
				
			||||||
 | 
					    gl.EnvironmentsListFolderApp.$destroy(true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gl.EnvironmentsListFolderApp = new EnvironmentsFolderComponent({
 | 
				
			||||||
 | 
					    el: document.querySelector('#environments-folder-list-view'),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,181 @@
 | 
				
			||||||
 | 
					/* eslint-disable no-param-reassign, no-new */
 | 
				
			||||||
 | 
					/* global Flash */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					Vue.use(require('vue-resource'));
 | 
				
			||||||
 | 
					const EnvironmentsService = require('../services/environments_service');
 | 
				
			||||||
 | 
					const EnvironmentTable = require('../components/environments_table');
 | 
				
			||||||
 | 
					const EnvironmentsStore = require('../stores/environments_store');
 | 
				
			||||||
 | 
					require('../../vue_shared/components/table_pagination');
 | 
				
			||||||
 | 
					require('../../lib/utils/common_utils');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = Vue.component('environment-folder-view', {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  components: {
 | 
				
			||||||
 | 
					    'environment-table': EnvironmentTable,
 | 
				
			||||||
 | 
					    'table-pagination': gl.VueGlPagination,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    const environmentsData = document.querySelector('#environments-folder-list-view').dataset;
 | 
				
			||||||
 | 
					    const store = new EnvironmentsStore();
 | 
				
			||||||
 | 
					    const pathname = window.location.pathname;
 | 
				
			||||||
 | 
					    const endpoint = `${pathname}.json`;
 | 
				
			||||||
 | 
					    const folderName = pathname.substr(pathname.lastIndexOf('/') + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      store,
 | 
				
			||||||
 | 
					      folderName,
 | 
				
			||||||
 | 
					      endpoint,
 | 
				
			||||||
 | 
					      state: store.state,
 | 
				
			||||||
 | 
					      visibility: 'available',
 | 
				
			||||||
 | 
					      isLoading: false,
 | 
				
			||||||
 | 
					      cssContainerClass: environmentsData.cssClass,
 | 
				
			||||||
 | 
					      canCreateDeployment: environmentsData.canCreateDeployment,
 | 
				
			||||||
 | 
					      canReadEnvironment: environmentsData.canReadEnvironment,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // svgs
 | 
				
			||||||
 | 
					      commitIconSvg: environmentsData.commitIconSvg,
 | 
				
			||||||
 | 
					      playIconSvg: environmentsData.playIconSvg,
 | 
				
			||||||
 | 
					      terminalIconSvg: environmentsData.terminalIconSvg,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Pagination Properties,
 | 
				
			||||||
 | 
					      paginationInformation: {},
 | 
				
			||||||
 | 
					      pageNumber: 1,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    scope() {
 | 
				
			||||||
 | 
					      return gl.utils.getParameterByName('scope');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canReadEnvironmentParsed() {
 | 
				
			||||||
 | 
					      return gl.utils.convertPermissionToBoolean(this.canReadEnvironment);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canCreateDeploymentParsed() {
 | 
				
			||||||
 | 
					      return gl.utils.convertPermissionToBoolean(this.canCreateDeployment);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * URL to link in the stopped tab.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {String}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    stoppedPath() {
 | 
				
			||||||
 | 
					      return `${window.location.pathname}?scope=stopped`;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * URL to link in the available tab.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {String}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    availablePath() {
 | 
				
			||||||
 | 
					      return window.location.pathname;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Fetches all the environments and stores them.
 | 
				
			||||||
 | 
					   * Toggles loading property.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  created() {
 | 
				
			||||||
 | 
					    const scope = gl.utils.getParameterByName('scope') || this.visibility;
 | 
				
			||||||
 | 
					    const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const service = new EnvironmentsService(endpoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.isLoading = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return service.all()
 | 
				
			||||||
 | 
					      .then(resp => ({
 | 
				
			||||||
 | 
					        headers: resp.headers,
 | 
				
			||||||
 | 
					        body: resp.json(),
 | 
				
			||||||
 | 
					      }))
 | 
				
			||||||
 | 
					      .then((response) => {
 | 
				
			||||||
 | 
					        this.store.storeAvailableCount(response.body.available_count);
 | 
				
			||||||
 | 
					        this.store.storeStoppedCount(response.body.stopped_count);
 | 
				
			||||||
 | 
					        this.store.storeEnvironments(response.body.environments);
 | 
				
			||||||
 | 
					        this.store.setPagination(response.headers);
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .then(() => {
 | 
				
			||||||
 | 
					        this.isLoading = false;
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch(() => {
 | 
				
			||||||
 | 
					        this.isLoading = false;
 | 
				
			||||||
 | 
					        new Flash('An error occurred while fetching the environments.', 'alert');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Will change the page number and update the URL.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  {Number} pageNumber desired page to go to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    changePage(pageNumber) {
 | 
				
			||||||
 | 
					      const param = gl.utils.setParamInURL('page', pageNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      gl.utils.visitUrl(param);
 | 
				
			||||||
 | 
					      return param;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  template: `
 | 
				
			||||||
 | 
					    <div :class="cssContainerClass">
 | 
				
			||||||
 | 
					      <div class="top-area" v-if="!isLoading">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <h4 class="js-folder-name environments-folder-name">
 | 
				
			||||||
 | 
					          Environments / <b>{{folderName}}</b>
 | 
				
			||||||
 | 
					        </h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <ul class="nav-links">
 | 
				
			||||||
 | 
					          <li v-bind:class="{ 'active': scope === null || scope === 'available' }">
 | 
				
			||||||
 | 
					            <a :href="availablePath" class="js-available-environments-folder-tab">
 | 
				
			||||||
 | 
					              Available
 | 
				
			||||||
 | 
					              <span class="badge js-available-environments-count">
 | 
				
			||||||
 | 
					                {{state.availableCounter}}
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          <li v-bind:class="{ 'active' : scope === 'stopped' }">
 | 
				
			||||||
 | 
					            <a :href="stoppedPath" class="js-stopped-environments-folder-tab">
 | 
				
			||||||
 | 
					              Stopped
 | 
				
			||||||
 | 
					              <span class="badge js-stopped-environments-count">
 | 
				
			||||||
 | 
					                {{state.stoppedCounter}}
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div class="environments-container">
 | 
				
			||||||
 | 
					        <div class="environments-list-loading text-center" v-if="isLoading">
 | 
				
			||||||
 | 
					          <i class="fa fa-spinner fa-spin"></i>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="table-holder"
 | 
				
			||||||
 | 
					          v-if="!isLoading && state.environments.length > 0">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <environment-table
 | 
				
			||||||
 | 
					            :environments="state.environments"
 | 
				
			||||||
 | 
					            :can-create-deployment="canCreateDeploymentParsed"
 | 
				
			||||||
 | 
					            :can-read-environment="canReadEnvironmentParsed"
 | 
				
			||||||
 | 
					            :play-icon-svg="playIconSvg"
 | 
				
			||||||
 | 
					            :terminal-icon-svg="terminalIconSvg"
 | 
				
			||||||
 | 
					            :commit-icon-svg="commitIconSvg">
 | 
				
			||||||
 | 
					          </environment-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
 | 
				
			||||||
 | 
					            :change="changePage"
 | 
				
			||||||
 | 
					            :pageInfo="state.paginationInformation">
 | 
				
			||||||
 | 
					          </table-pagination>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  `,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,8 @@
 | 
				
			||||||
/* globals Vue */
 | 
					const Vue = require('vue');
 | 
				
			||||||
/* eslint-disable no-unused-vars, no-param-reassign */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EnvironmentsService {
 | 
					class EnvironmentsService {
 | 
				
			||||||
 | 
					  constructor(endpoint) {
 | 
				
			||||||
  constructor(root) {
 | 
					    this.environments = Vue.resource(endpoint);
 | 
				
			||||||
    Vue.http.options.root = root;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.environments = Vue.resource(root);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Vue.http.interceptors.push((request, next) => {
 | 
					 | 
				
			||||||
      // needed in order to not break the tests.
 | 
					 | 
				
			||||||
      if ($.rails) {
 | 
					 | 
				
			||||||
        request.headers['X-CSRF-Token'] = $.rails.csrfToken();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      next();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  all() {
 | 
					  all() {
 | 
				
			||||||
| 
						 | 
					@ -22,4 +10,4 @@ class EnvironmentsService {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.EnvironmentsService = EnvironmentsService;
 | 
					module.exports = EnvironmentsService;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,190 +1,90 @@
 | 
				
			||||||
/* eslint-disable no-param-reassign */
 | 
					require('~/lib/utils/common_utils');
 | 
				
			||||||
(() => {
 | 
					/**
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					 * Environments Store.
 | 
				
			||||||
  window.gl.environmentsList = window.gl.environmentsList || {};
 | 
					 *
 | 
				
			||||||
 | 
					 * Stores received environments, count of stopped environments and count of
 | 
				
			||||||
 | 
					 * available environments.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class EnvironmentsStore {
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.state = {};
 | 
				
			||||||
 | 
					    this.state.environments = [];
 | 
				
			||||||
 | 
					    this.state.stoppedCounter = 0;
 | 
				
			||||||
 | 
					    this.state.availableCounter = 0;
 | 
				
			||||||
 | 
					    this.state.paginationInformation = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gl.environmentsList.EnvironmentsStore = {
 | 
					    return this;
 | 
				
			||||||
    state: {},
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    create() {
 | 
					  /**
 | 
				
			||||||
      this.state.environments = [];
 | 
					   *
 | 
				
			||||||
      this.state.stoppedCounter = 0;
 | 
					   * Stores the received environments.
 | 
				
			||||||
      this.state.availableCounter = 0;
 | 
					   *
 | 
				
			||||||
      this.state.visibility = 'available';
 | 
					   * In the main environments endpoint, each environment has the following schema
 | 
				
			||||||
      this.state.filteredEnvironments = [];
 | 
					   * { name: String, size: Number, latest: Object }
 | 
				
			||||||
 | 
					   * In the endpoint to retrieve environments from each folder, the environment does
 | 
				
			||||||
 | 
					   * not have the `latest` key and the data is all in the root level.
 | 
				
			||||||
 | 
					   * To avoid doing this check in the view, we store both cases the same by extracting
 | 
				
			||||||
 | 
					   * what is inside the `latest` key.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * If the `size` is bigger than 1, it means it should be rendered as a folder.
 | 
				
			||||||
 | 
					   * In those cases we add `isFolder` key in order to render it properly.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param  {Array} environments
 | 
				
			||||||
 | 
					   * @returns {Array}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  storeEnvironments(environments = []) {
 | 
				
			||||||
 | 
					    const filteredEnvironments = environments.map((env) => {
 | 
				
			||||||
 | 
					      let filtered = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return this;
 | 
					      if (env.size > 1) {
 | 
				
			||||||
    },
 | 
					        filtered = Object.assign({}, env, { isFolder: true, folderName: env.name });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					      if (env.latest) {
 | 
				
			||||||
     * In order to display a tree view we need to modify the received
 | 
					        filtered = Object.assign(filtered, env, env.latest);
 | 
				
			||||||
     * data in to a tree structure based on `environment_type`
 | 
					        delete filtered.latest;
 | 
				
			||||||
     * sorted alphabetically.
 | 
					      } else {
 | 
				
			||||||
     * In each children a `vue-` property will be added. This property will be
 | 
					        filtered = Object.assign(filtered, env);
 | 
				
			||||||
     * used to know if an item is a children mostly for css purposes. This is
 | 
					      }
 | 
				
			||||||
     * needed because the children row is a fragment instance and therfore does
 | 
					 | 
				
			||||||
     * not accept non-prop attributes.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @example
 | 
					 | 
				
			||||||
     * it will transform this:
 | 
					 | 
				
			||||||
     * [
 | 
					 | 
				
			||||||
     *   { name: "environment", environment_type: "review" },
 | 
					 | 
				
			||||||
     *   { name: "environment_1", environment_type: null }
 | 
					 | 
				
			||||||
     *   { name: "environment_2, environment_type: "review" }
 | 
					 | 
				
			||||||
     * ]
 | 
					 | 
				
			||||||
     * into this:
 | 
					 | 
				
			||||||
     * [
 | 
					 | 
				
			||||||
     *   { name: "review", children:
 | 
					 | 
				
			||||||
     *      [
 | 
					 | 
				
			||||||
     *        { name: "environment", environment_type: "review", vue-isChildren: true},
 | 
					 | 
				
			||||||
     *        { name: "environment_2", environment_type: "review", vue-isChildren: true}
 | 
					 | 
				
			||||||
     *      ]
 | 
					 | 
				
			||||||
     *   },
 | 
					 | 
				
			||||||
     *  {name: "environment_1", environment_type: null}
 | 
					 | 
				
			||||||
     * ]
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param  {Array} environments List of environments.
 | 
					 | 
				
			||||||
     * @returns {Array} Tree structured array with the received environments.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    storeEnvironments(environments = []) {
 | 
					 | 
				
			||||||
      this.state.stoppedCounter = this.countByState(environments, 'stopped');
 | 
					 | 
				
			||||||
      this.state.availableCounter = this.countByState(environments, 'available');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const environmentsTree = environments.reduce((acc, environment) => {
 | 
					      return filtered;
 | 
				
			||||||
        if (environment.environment_type !== null) {
 | 
					    });
 | 
				
			||||||
          const occurs = acc.filter(element => element.children &&
 | 
					 | 
				
			||||||
             element.name === environment.environment_type);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          environment['vue-isChildren'] = true;
 | 
					    this.state.environments = filteredEnvironments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (occurs.length) {
 | 
					    return filteredEnvironments;
 | 
				
			||||||
            acc[acc.indexOf(occurs[0])].children.push(environment);
 | 
					  }
 | 
				
			||||||
            acc[acc.indexOf(occurs[0])].children.slice().sort(this.sortByName);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            acc.push({
 | 
					 | 
				
			||||||
              name: environment.environment_type,
 | 
					 | 
				
			||||||
              children: [environment],
 | 
					 | 
				
			||||||
              isOpen: false,
 | 
					 | 
				
			||||||
              'vue-isChildren': environment['vue-isChildren'],
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          acc.push(environment);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return acc;
 | 
					  setPagination(pagination = {}) {
 | 
				
			||||||
      }, []).slice().sort(this.sortByName);
 | 
					    const normalizedHeaders = gl.utils.normalizeHeaders(pagination);
 | 
				
			||||||
 | 
					    const paginationInformation = gl.utils.parseIntPagination(normalizedHeaders);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.state.environments = environmentsTree;
 | 
					    this.state.paginationInformation = paginationInformation;
 | 
				
			||||||
 | 
					    return paginationInformation;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.filterEnvironmentsByVisibility(this.state.environments);
 | 
					  /**
 | 
				
			||||||
 | 
					   * Stores the number of available environments.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param  {Number} count = 0
 | 
				
			||||||
 | 
					   * @return {Number}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  storeAvailableCount(count = 0) {
 | 
				
			||||||
 | 
					    this.state.availableCounter = count;
 | 
				
			||||||
 | 
					    return count;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return environmentsTree;
 | 
					  /**
 | 
				
			||||||
    },
 | 
					   * Stores the number of closed environments.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param  {Number} count = 0
 | 
				
			||||||
 | 
					   * @return {Number}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  storeStoppedCount(count = 0) {
 | 
				
			||||||
 | 
					    this.state.stoppedCounter = count;
 | 
				
			||||||
 | 
					    return count;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    storeVisibility(visibility) {
 | 
					module.exports = EnvironmentsStore;
 | 
				
			||||||
      this.state.visibility = visibility;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Given the visibility prop provided by the url query parameter and which
 | 
					 | 
				
			||||||
     * changes according to the active tab we need to filter which environments
 | 
					 | 
				
			||||||
     * should be visible.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * The environments array is a recursive tree structure and we need to filter
 | 
					 | 
				
			||||||
     * both root level environments and children environments.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * In order to acomplish that, both `filterState` and `filterEnvironmentsByVisibility`
 | 
					 | 
				
			||||||
     * functions work together.
 | 
					 | 
				
			||||||
     * The first one works as the filter that verifies if the given environment matches
 | 
					 | 
				
			||||||
     * the given state.
 | 
					 | 
				
			||||||
     * The second guarantees both root level and children elements are filtered as well.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * Given array of environments will return only
 | 
					 | 
				
			||||||
     * the environments that match the state stored.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param {Array} array
 | 
					 | 
				
			||||||
     * @return {Array}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    filterEnvironmentsByVisibility(arr) {
 | 
					 | 
				
			||||||
      const filteredEnvironments = arr.map((item) => {
 | 
					 | 
				
			||||||
        if (item.children) {
 | 
					 | 
				
			||||||
          const filteredChildren = this.filterEnvironmentsByVisibility(
 | 
					 | 
				
			||||||
            item.children,
 | 
					 | 
				
			||||||
          ).filter(Boolean);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (filteredChildren.length) {
 | 
					 | 
				
			||||||
            item.children = filteredChildren;
 | 
					 | 
				
			||||||
            return item;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this.filterState(this.state.visibility, item);
 | 
					 | 
				
			||||||
      }).filter(Boolean);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      this.state.filteredEnvironments = filteredEnvironments;
 | 
					 | 
				
			||||||
      return filteredEnvironments;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Given the state and the environment,
 | 
					 | 
				
			||||||
     * returns only if the environment state matches the one provided.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param  {String} state
 | 
					 | 
				
			||||||
     * @param  {Object} environment
 | 
					 | 
				
			||||||
     * @return {Object}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    filterState(state, environment) {
 | 
					 | 
				
			||||||
      return environment.state === state && environment;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Toggles folder open property given the environment type.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param  {String} envType
 | 
					 | 
				
			||||||
     * @return {Array}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    toggleFolder(envType) {
 | 
					 | 
				
			||||||
      const environments = this.state.environments;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const environmentsCopy = environments.map((env) => {
 | 
					 | 
				
			||||||
        if (env['vue-isChildren'] && env.name === envType) {
 | 
					 | 
				
			||||||
          env.isOpen = !env.isOpen;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return env;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      this.state.environments = environmentsCopy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return environmentsCopy;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Given an array of environments, returns the number of environments
 | 
					 | 
				
			||||||
     * that have the given state.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param  {Array} environments
 | 
					 | 
				
			||||||
     * @param  {String} state
 | 
					 | 
				
			||||||
     * @returns {Number}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    countByState(environments, state) {
 | 
					 | 
				
			||||||
      return environments.filter(env => env.state === state).length;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Sorts the two objects provided by their name.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param  {Object} a
 | 
					 | 
				
			||||||
     * @param  {Object} b
 | 
					 | 
				
			||||||
     * @returns {Number}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    sortByName(a, b) {
 | 
					 | 
				
			||||||
      const nameA = a.name.toUpperCase();
 | 
					 | 
				
			||||||
      const nameB = b.name.toUpperCase();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; // eslint-disable-line
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -231,6 +231,21 @@
 | 
				
			||||||
      return upperCaseHeaders;
 | 
					      return upperCaseHeaders;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parses pagination object string values into numbers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param {Object} paginationInformation
 | 
				
			||||||
 | 
					     * @returns {Object}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    w.gl.utils.parseIntPagination = paginationInformation => ({
 | 
				
			||||||
 | 
					      perPage: parseInt(paginationInformation['X-PER-PAGE'], 10),
 | 
				
			||||||
 | 
					      page: parseInt(paginationInformation['X-PAGE'], 10),
 | 
				
			||||||
 | 
					      total: parseInt(paginationInformation['X-TOTAL'], 10),
 | 
				
			||||||
 | 
					      totalPages: parseInt(paginationInformation['X-TOTAL-PAGES'], 10),
 | 
				
			||||||
 | 
					      nextPage: parseInt(paginationInformation['X-NEXT-PAGE'], 10),
 | 
				
			||||||
 | 
					      previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10),
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Transforms a DOMStringMap into a plain object.
 | 
					     * Transforms a DOMStringMap into a plain object.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					@ -241,5 +256,45 @@
 | 
				
			||||||
      acc[element] = DOMStringMapObject[element];
 | 
					      acc[element] = DOMStringMapObject[element];
 | 
				
			||||||
      return acc;
 | 
					      return acc;
 | 
				
			||||||
    }, {});
 | 
					    }, {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the search parameter of a URL given the parameter and values provided.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * If no search params are present we'll add it.
 | 
				
			||||||
 | 
					     * If param for page is already present, we'll update it
 | 
				
			||||||
 | 
					     * If there are params but not for the given one, we'll add it at the end.
 | 
				
			||||||
 | 
					     * Returns the new search parameters.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param {String} param
 | 
				
			||||||
 | 
					     * @param {Number|String|Undefined|Null} value
 | 
				
			||||||
 | 
					     * @return {String}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    w.gl.utils.setParamInURL = (param, value) => {
 | 
				
			||||||
 | 
					      let search;
 | 
				
			||||||
 | 
					      const locationSearch = window.location.search;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (locationSearch.length === 0) {
 | 
				
			||||||
 | 
					        search = `?${param}=${value}`;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (locationSearch.indexOf(param) !== -1) {
 | 
				
			||||||
 | 
					        const regex = new RegExp(param + '=\\d');
 | 
				
			||||||
 | 
					        search = locationSearch.replace(regex, `${param}=${value}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (locationSearch.length && locationSearch.indexOf(param) === -1) {
 | 
				
			||||||
 | 
					        search = `${locationSearch}&${param}=${value}`;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return search;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts permission provided as strings to booleans.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  {String} string
 | 
				
			||||||
 | 
					     * @returns {Boolean}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    w.gl.utils.convertPermissionToBoolean = permission => permission === 'true';
 | 
				
			||||||
  })(window);
 | 
					  })(window);
 | 
				
			||||||
}).call(this);
 | 
					}).call(this);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,16 @@ require('../vue_shared/components/pipelines_table');
 | 
				
			||||||
      this.store.fetchDataLoop.call(this, Vue, this.pagenum, this.scope, this.apiScope);
 | 
					      this.store.fetchDataLoop.call(this, Vue, this.pagenum, this.scope, this.apiScope);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
      change(pagenum, apiScope) {
 | 
					
 | 
				
			||||||
 | 
					      /**
 | 
				
			||||||
 | 
					       * Changes the URL according to the pagination component.
 | 
				
			||||||
 | 
					       *
 | 
				
			||||||
 | 
					       * If no scope is provided, 'all' is assumed.
 | 
				
			||||||
 | 
					       *
 | 
				
			||||||
 | 
					       * @param  {Number} pagenum
 | 
				
			||||||
 | 
					       * @param  {String} apiScope = 'all'
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      change(pagenum, apiScope = 'all') {
 | 
				
			||||||
        gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
 | 
					        gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,16 +5,7 @@ require('../vue_realtime_listener');
 | 
				
			||||||
((gl) => {
 | 
					((gl) => {
 | 
				
			||||||
  const pageValues = (headers) => {
 | 
					  const pageValues = (headers) => {
 | 
				
			||||||
    const normalized = gl.utils.normalizeHeaders(headers);
 | 
					    const normalized = gl.utils.normalizeHeaders(headers);
 | 
				
			||||||
 | 
					    const paginationInfo = gl.utils.normalizeHeaders(normalized);
 | 
				
			||||||
    const paginationInfo = {
 | 
					 | 
				
			||||||
      perPage: +normalized['X-PER-PAGE'],
 | 
					 | 
				
			||||||
      page: +normalized['X-PAGE'],
 | 
					 | 
				
			||||||
      total: +normalized['X-TOTAL'],
 | 
					 | 
				
			||||||
      totalPages: +normalized['X-TOTAL-PAGES'],
 | 
					 | 
				
			||||||
      nextPage: +normalized['X-NEXT-PAGE'],
 | 
					 | 
				
			||||||
      previousPage: +normalized['X-PREV-PAGE'],
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return paginationInfo;
 | 
					    return paginationInfo;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
/* global Vue */
 | 
					/* global Vue */
 | 
				
			||||||
 | 
					window.Vue = require('vue');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(() => {
 | 
					(() => {
 | 
				
			||||||
  window.gl = window.gl || {};
 | 
					  window.gl = window.gl || {};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,9 +57,7 @@ window.Vue = require('vue');
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
      changePage(e) {
 | 
					      changePage(e) {
 | 
				
			||||||
        let apiScope = gl.utils.getParameterByName('scope');
 | 
					        const apiScope = gl.utils.getParameterByName('scope');
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!apiScope) apiScope = 'all';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const text = e.target.innerText;
 | 
					        const text = e.target.innerText;
 | 
				
			||||||
        const { totalPages, nextPage, previousPage } = this.pageInfo;
 | 
					        const { totalPages, nextPage, previousPage } = this.pageInfo;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,11 @@
 | 
				
			||||||
  font-size: 34px;
 | 
					  font-size: 34px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.environments-folder-name {
 | 
				
			||||||
 | 
					  font-weight: normal;
 | 
				
			||||||
 | 
					  padding-top: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (max-width: $screen-xs-max) {
 | 
					@media (max-width: $screen-xs-max) {
 | 
				
			||||||
  .environments-container {
 | 
					  .environments-container {
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
| 
						 | 
					@ -110,17 +115,20 @@
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .children-row .environment-name {
 | 
					 | 
				
			||||||
    margin-left: 17px;
 | 
					 | 
				
			||||||
    margin-right: -17px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .folder-icon {
 | 
					  .folder-icon {
 | 
				
			||||||
    padding: 0 5px 0 0;
 | 
					    margin-right: 3px;
 | 
				
			||||||
 | 
					    color: $gl-text-color-secondary;
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .fa:nth-child(1) {
 | 
				
			||||||
 | 
					      margin-right: 3px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .folder-name {
 | 
					  .folder-name {
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    color: $gl-text-color-secondary;
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,4 +143,4 @@
 | 
				
			||||||
      margin-right: 0;
 | 
					      margin-right: 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,15 +9,40 @@ class Projects::EnvironmentsController < Projects::ApplicationController
 | 
				
			||||||
  before_action :verify_api_request!, only: :terminal_websocket_authorize
 | 
					  before_action :verify_api_request!, only: :terminal_websocket_authorize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def index
 | 
					  def index
 | 
				
			||||||
    @scope = params[:scope]
 | 
					    @environments = project.environments
 | 
				
			||||||
    @environments = project.environments.includes(:last_deployment)
 | 
					      .with_state(params[:scope] || :available)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.html
 | 
					      format.html
 | 
				
			||||||
      format.json do
 | 
					      format.json do
 | 
				
			||||||
        render json: EnvironmentSerializer
 | 
					        render json: {
 | 
				
			||||||
          .new(project: @project, user: current_user)
 | 
					          environments: EnvironmentSerializer
 | 
				
			||||||
          .represent(@environments)
 | 
					            .new(project: @project, user: @current_user)
 | 
				
			||||||
 | 
					            .with_pagination(request, response)
 | 
				
			||||||
 | 
					            .within_folders
 | 
				
			||||||
 | 
					            .represent(@environments),
 | 
				
			||||||
 | 
					          available_count: project.environments.available.count,
 | 
				
			||||||
 | 
					          stopped_count: project.environments.stopped.count
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def folder
 | 
				
			||||||
 | 
					    folder_environments = project.environments.where(environment_type: params[:id])
 | 
				
			||||||
 | 
					    @environments = folder_environments.with_state(params[:scope] || :available)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    respond_to do |format|
 | 
				
			||||||
 | 
					      format.html
 | 
				
			||||||
 | 
					      format.json do
 | 
				
			||||||
 | 
					        render json: {
 | 
				
			||||||
 | 
					          environments: EnvironmentSerializer
 | 
				
			||||||
 | 
					            .new(project: @project, user: @current_user)
 | 
				
			||||||
 | 
					            .with_pagination(request, response)
 | 
				
			||||||
 | 
					            .represent(@environments),
 | 
				
			||||||
 | 
					          available_count: folder_environments.available.count,
 | 
				
			||||||
 | 
					          stopped_count: folder_environments.stopped.count
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,8 +20,6 @@ class EnvironmentSerializer < BaseSerializer
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def represent(resource, opts = {})
 | 
					  def represent(resource, opts = {})
 | 
				
			||||||
    resource = @paginator.paginate(resource) if paginated?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if itemized?
 | 
					    if itemized?
 | 
				
			||||||
      itemize(resource).map do |item|
 | 
					      itemize(resource).map do |item|
 | 
				
			||||||
        { name: item.name,
 | 
					        { name: item.name,
 | 
				
			||||||
| 
						 | 
					@ -29,6 +27,8 @@ class EnvironmentSerializer < BaseSerializer
 | 
				
			||||||
          latest: super(item.latest, opts) }
 | 
					          latest: super(item.latest, opts) }
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
 | 
					      resource = @paginator.paginate(resource) if paginated?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      super(resource, opts)
 | 
					      super(resource, opts)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					@ -36,15 +36,20 @@ class EnvironmentSerializer < BaseSerializer
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def itemize(resource)
 | 
					  def itemize(resource)
 | 
				
			||||||
    items = resource.group(:item_name).order('item_name ASC')
 | 
					    items = resource.order('folder_name ASC')
 | 
				
			||||||
      .pluck('COALESCE(environment_type, name) AS item_name',
 | 
					      .group('COALESCE(environment_type, name)')
 | 
				
			||||||
             'COUNT(*) AS environments_count',
 | 
					      .select('COALESCE(environment_type, name) AS folder_name',
 | 
				
			||||||
             'MAX(id) AS last_environment_id')
 | 
					              'COUNT(*) AS size', 'MAX(id) AS last_id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    environments = resource.where(id: items.map(&:last)).index_by(&:id)
 | 
					    # It makes a difference when you call `paginate` method, because
 | 
				
			||||||
 | 
					    # although `page` is effective at the end, it calls counting methods
 | 
				
			||||||
 | 
					    # immediately.
 | 
				
			||||||
 | 
					    items = @paginator.paginate(items) if paginated?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    items.map do |name, size, id|
 | 
					    environments = resource.where(id: items.map(&:last_id)).index_by(&:id)
 | 
				
			||||||
      Item.new(name, size, environments[id])
 | 
					
 | 
				
			||||||
 | 
					    items.map do |item|
 | 
				
			||||||
 | 
					      Item.new(item.folder_name, item.size, environments[item.last_id])
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					- @no_container = true
 | 
				
			||||||
 | 
					- page_title "Environments"
 | 
				
			||||||
 | 
					= render "projects/pipelines/head"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- content_for :page_specific_javascripts do
 | 
				
			||||||
 | 
					  = page_specific_javascript_bundle_tag("environments_folder")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#environments-folder-list-view{ data: { "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
 | 
				
			||||||
 | 
					  "can-read-environment" => can?(current_user, :read_environment, @project).to_s,
 | 
				
			||||||
 | 
					  "css-class" => container_class,
 | 
				
			||||||
 | 
					  "commit-icon-svg" => custom_icon("icon_commit"),
 | 
				
			||||||
 | 
					  "terminal-icon-svg" => custom_icon("icon_terminal"),
 | 
				
			||||||
 | 
					  "play-icon-svg" => custom_icon("icon_play") } }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					title: Adds paginationd and folders view to environments table
 | 
				
			||||||
 | 
					merge_request:
 | 
				
			||||||
 | 
					author:
 | 
				
			||||||
| 
						 | 
					@ -156,6 +156,10 @@ constraints(ProjectUrlConstrainer.new) do
 | 
				
			||||||
          get :terminal
 | 
					          get :terminal
 | 
				
			||||||
          get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
 | 
					          get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        collection do
 | 
				
			||||||
 | 
					          get :folder, path: 'folders/:id'
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      resource :cycle_analytics, only: [:show]
 | 
					      resource :cycle_analytics, only: [:show]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ var config = {
 | 
				
			||||||
    commit_pipelines:     './commit/pipelines/pipelines_bundle.js',
 | 
					    commit_pipelines:     './commit/pipelines/pipelines_bundle.js',
 | 
				
			||||||
    diff_notes:           './diff_notes/diff_notes_bundle.js',
 | 
					    diff_notes:           './diff_notes/diff_notes_bundle.js',
 | 
				
			||||||
    environments:         './environments/environments_bundle.js',
 | 
					    environments:         './environments/environments_bundle.js',
 | 
				
			||||||
 | 
					    environments_folder:  './environments/folder/environments_folder_bundle.js',
 | 
				
			||||||
    filtered_search:      './filtered_search/filtered_search_bundle.js',
 | 
					    filtered_search:      './filtered_search/filtered_search_bundle.js',
 | 
				
			||||||
    graphs:               './graphs/graphs_bundle.js',
 | 
					    graphs:               './graphs/graphs_bundle.js',
 | 
				
			||||||
    issuable:             './issuable/issuable_bundle.js',
 | 
					    issuable:             './issuable/issuable_bundle.js',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,9 +3,12 @@ require 'spec_helper'
 | 
				
			||||||
describe Projects::EnvironmentsController do
 | 
					describe Projects::EnvironmentsController do
 | 
				
			||||||
  include ApiHelpers
 | 
					  include ApiHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let(:environment) { create(:environment) }
 | 
					  let(:user) { create(:user) }
 | 
				
			||||||
  let(:project)     { environment.project }
 | 
					  let(:project) { create(:empty_project) }
 | 
				
			||||||
  let(:user)        { create(:user) }
 | 
					
 | 
				
			||||||
 | 
					  let(:environment) do
 | 
				
			||||||
 | 
					    create(:environment, name: 'production', project: project)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before do
 | 
					  before do
 | 
				
			||||||
    project.team << [user, :master]
 | 
					    project.team << [user, :master]
 | 
				
			||||||
| 
						 | 
					@ -22,14 +25,58 @@ describe Projects::EnvironmentsController do
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'when requesting JSON response' do
 | 
					    context 'when requesting JSON response for folders' do
 | 
				
			||||||
      it 'responds with correct JSON' do
 | 
					      before do
 | 
				
			||||||
        get :index, environment_params(format: :json)
 | 
					        create(:environment, project: project,
 | 
				
			||||||
 | 
					                             name: 'staging/review-1',
 | 
				
			||||||
 | 
					                             state: :available)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        first_environment = json_response.first
 | 
					        create(:environment, project: project,
 | 
				
			||||||
 | 
					                             name: 'staging/review-2',
 | 
				
			||||||
 | 
					                             state: :available)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        expect(first_environment).not_to be_empty
 | 
					        create(:environment, project: project,
 | 
				
			||||||
        expect(first_environment['name']). to eq environment.name
 | 
					                             name: 'staging/review-3',
 | 
				
			||||||
 | 
					                             state: :stopped)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let(:environments) { json_response['environments'] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when requesting available environments scope' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          get :index, environment_params(format: :json, scope: :available)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'responds with a payload describing available environments' do
 | 
				
			||||||
 | 
					          expect(environments.count).to eq 2
 | 
				
			||||||
 | 
					          expect(environments.first['name']).to eq 'production'
 | 
				
			||||||
 | 
					          expect(environments.second['name']).to eq 'staging'
 | 
				
			||||||
 | 
					          expect(environments.second['size']).to eq 2
 | 
				
			||||||
 | 
					          expect(environments.second['latest']['name']).to eq 'staging/review-2'
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'contains values describing environment scopes sizes' do
 | 
				
			||||||
 | 
					          expect(json_response['available_count']).to eq 3
 | 
				
			||||||
 | 
					          expect(json_response['stopped_count']).to eq 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when requesting stopped environments scope' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          get :index, environment_params(format: :json, scope: :stopped)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'responds with a payload describing stopped environments' do
 | 
				
			||||||
 | 
					          expect(environments.count).to eq 1
 | 
				
			||||||
 | 
					          expect(environments.first['name']).to eq 'staging'
 | 
				
			||||||
 | 
					          expect(environments.first['size']).to eq 1
 | 
				
			||||||
 | 
					          expect(environments.first['latest']['name']).to eq 'staging/review-3'
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'contains values describing environment scopes sizes' do
 | 
				
			||||||
 | 
					          expect(json_response['available_count']).to eq 3
 | 
				
			||||||
 | 
					          expect(json_response['stopped_count']).to eq 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -275,7 +275,7 @@ feature 'Builds', :feature do
 | 
				
			||||||
        let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) }
 | 
					        let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) }
 | 
				
			||||||
        let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
 | 
					        let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it 'shows a link to lastest deployment' do
 | 
					        it 'shows a link to latest deployment' do
 | 
				
			||||||
          visit namespace_project_build_path(project.namespace, project, build)
 | 
					          visit namespace_project_build_path(project.namespace, project, build)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          expect(page).to have_link('latest deployment')
 | 
					          expect(page).to have_link('latest deployment')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
require('~/environments/components/environment_actions');
 | 
					const ActionsComponent = require('~/environments/components/environment_actions');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Actions Component', () => {
 | 
					describe('Actions Component', () => {
 | 
				
			||||||
  preloadFixtures('static/environments/element.html.raw');
 | 
					  preloadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ describe('Actions Component', () => {
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const component = new window.gl.environmentsList.ActionsComponent({
 | 
					    const component = new ActionsComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        actions: actionsMock,
 | 
					        actions: actionsMock,
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ describe('Actions Component', () => {
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const component = new window.gl.environmentsList.ActionsComponent({
 | 
					    const component = new ActionsComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        actions: actionsMock,
 | 
					        actions: actionsMock,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
require('~/environments/components/environment_external_url');
 | 
					const ExternalUrlComponent = require('~/environments/components/environment_external_url');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('External URL Component', () => {
 | 
					describe('External URL Component', () => {
 | 
				
			||||||
  preloadFixtures('static/environments/element.html.raw');
 | 
					  preloadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ describe('External URL Component', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should link to the provided externalUrl prop', () => {
 | 
					  it('should link to the provided externalUrl prop', () => {
 | 
				
			||||||
    const externalURL = 'https://gitlab.com';
 | 
					    const externalURL = 'https://gitlab.com';
 | 
				
			||||||
    const component = new window.gl.environmentsList.ExternalUrlComponent({
 | 
					    const component = new ExternalUrlComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        externalUrl: externalURL,
 | 
					        externalUrl: externalURL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
window.timeago = require('timeago.js');
 | 
					window.timeago = require('timeago.js');
 | 
				
			||||||
require('~/environments/components/environment_item');
 | 
					const EnvironmentItem = require('~/environments/components/environment_item');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Environment item', () => {
 | 
					describe('Environment item', () => {
 | 
				
			||||||
  preloadFixtures('static/environments/table.html.raw');
 | 
					  preloadFixtures('static/environments/table.html.raw');
 | 
				
			||||||
| 
						 | 
					@ -14,33 +14,16 @@ describe('Environment item', () => {
 | 
				
			||||||
    beforeEach(() => {
 | 
					    beforeEach(() => {
 | 
				
			||||||
      mockItem = {
 | 
					      mockItem = {
 | 
				
			||||||
        name: 'review',
 | 
					        name: 'review',
 | 
				
			||||||
        children: [
 | 
					        folderName: 'review',
 | 
				
			||||||
          {
 | 
					        size: 3,
 | 
				
			||||||
            name: 'review-app',
 | 
					        isFolder: true,
 | 
				
			||||||
            id: 1,
 | 
					        environment_path: 'url',
 | 
				
			||||||
            state: 'available',
 | 
					 | 
				
			||||||
            external_url: '',
 | 
					 | 
				
			||||||
            last_deployment: {},
 | 
					 | 
				
			||||||
            created_at: '2016-11-07T11:11:16.525Z',
 | 
					 | 
				
			||||||
            updated_at: '2016-11-10T15:55:58.778Z',
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            name: 'production',
 | 
					 | 
				
			||||||
            id: 2,
 | 
					 | 
				
			||||||
            state: 'available',
 | 
					 | 
				
			||||||
            external_url: '',
 | 
					 | 
				
			||||||
            last_deployment: {},
 | 
					 | 
				
			||||||
            created_at: '2016-11-07T11:11:16.525Z',
 | 
					 | 
				
			||||||
            updated_at: '2016-11-10T15:55:58.778Z',
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      component = new window.gl.environmentsList.EnvironmentItem({
 | 
					      component = new EnvironmentItem({
 | 
				
			||||||
        el: document.querySelector('tr#environment-row'),
 | 
					        el: document.querySelector('tr#environment-row'),
 | 
				
			||||||
        propsData: {
 | 
					        propsData: {
 | 
				
			||||||
          model: mockItem,
 | 
					          model: mockItem,
 | 
				
			||||||
          toggleRow: () => {},
 | 
					 | 
				
			||||||
          canCreateDeployment: false,
 | 
					          canCreateDeployment: false,
 | 
				
			||||||
          canReadEnvironment: true,
 | 
					          canReadEnvironment: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
| 
						 | 
					@ -53,7 +36,7 @@ describe('Environment item', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('Should render the number of children in a badge', () => {
 | 
					    it('Should render the number of children in a badge', () => {
 | 
				
			||||||
      expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.children.length);
 | 
					      expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.size);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,8 +46,8 @@ describe('Environment item', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    beforeEach(() => {
 | 
					    beforeEach(() => {
 | 
				
			||||||
      environment = {
 | 
					      environment = {
 | 
				
			||||||
        id: 31,
 | 
					 | 
				
			||||||
        name: 'production',
 | 
					        name: 'production',
 | 
				
			||||||
 | 
					        size: 1,
 | 
				
			||||||
        state: 'stopped',
 | 
					        state: 'stopped',
 | 
				
			||||||
        external_url: 'http://external.com',
 | 
					        external_url: 'http://external.com',
 | 
				
			||||||
        environment_type: null,
 | 
					        environment_type: null,
 | 
				
			||||||
| 
						 | 
					@ -125,11 +108,10 @@ describe('Environment item', () => {
 | 
				
			||||||
        updated_at: '2016-11-10T15:55:58.778Z',
 | 
					        updated_at: '2016-11-10T15:55:58.778Z',
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      component = new window.gl.environmentsList.EnvironmentItem({
 | 
					      component = new EnvironmentItem({
 | 
				
			||||||
        el: document.querySelector('tr#environment-row'),
 | 
					        el: document.querySelector('tr#environment-row'),
 | 
				
			||||||
        propsData: {
 | 
					        propsData: {
 | 
				
			||||||
          model: environment,
 | 
					          model: environment,
 | 
				
			||||||
          toggleRow: () => {},
 | 
					 | 
				
			||||||
          canCreateDeployment: true,
 | 
					          canCreateDeployment: true,
 | 
				
			||||||
          canReadEnvironment: true,
 | 
					          canReadEnvironment: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
require('~/environments/components/environment_rollback');
 | 
					const RollbackComponent = require('~/environments/components/environment_rollback');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Rollback Component', () => {
 | 
					describe('Rollback Component', () => {
 | 
				
			||||||
  preloadFixtures('static/environments/element.html.raw');
 | 
					  preloadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ describe('Rollback Component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('Should link to the provided retryUrl', () => {
 | 
					  it('Should link to the provided retryUrl', () => {
 | 
				
			||||||
    const component = new window.gl.environmentsList.RollbackComponent({
 | 
					    const component = new RollbackComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        retryUrl: retryURL,
 | 
					        retryUrl: retryURL,
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ describe('Rollback Component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('Should render Re-deploy label when isLastDeployment is true', () => {
 | 
					  it('Should render Re-deploy label when isLastDeployment is true', () => {
 | 
				
			||||||
    const component = new window.gl.environmentsList.RollbackComponent({
 | 
					    const component = new RollbackComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        retryUrl: retryURL,
 | 
					        retryUrl: retryURL,
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ describe('Rollback Component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('Should render Rollback label when isLastDeployment is false', () => {
 | 
					  it('Should render Rollback label when isLastDeployment is false', () => {
 | 
				
			||||||
    const component = new window.gl.environmentsList.RollbackComponent({
 | 
					    const component = new RollbackComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        retryUrl: retryURL,
 | 
					        retryUrl: retryURL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,7 @@
 | 
				
			||||||
/* global Vue, environment */
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					 | 
				
			||||||
require('~/flash');
 | 
					require('~/flash');
 | 
				
			||||||
require('~/environments/stores/environments_store');
 | 
					const EnvironmentsComponent = require('~/environments/components/environment');
 | 
				
			||||||
require('~/environments/components/environment');
 | 
					const { environment } = require('./mock_data');
 | 
				
			||||||
require('./mock_data');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Environment', () => {
 | 
					describe('Environment', () => {
 | 
				
			||||||
  preloadFixtures('static/environments/environments.html.raw');
 | 
					  preloadFixtures('static/environments/environments.html.raw');
 | 
				
			||||||
| 
						 | 
					@ -33,11 +31,8 @@ describe('Environment', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('should render the empty state', (done) => {
 | 
					      it('should render the empty state', (done) => {
 | 
				
			||||||
        component = new gl.environmentsList.EnvironmentsComponent({
 | 
					        component = new EnvironmentsComponent({
 | 
				
			||||||
          el: document.querySelector('#environments-list-view'),
 | 
					          el: document.querySelector('#environments-list-view'),
 | 
				
			||||||
          propsData: {
 | 
					 | 
				
			||||||
            store: gl.environmentsList.EnvironmentsStore.create(),
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setTimeout(() => {
 | 
					        setTimeout(() => {
 | 
				
			||||||
| 
						 | 
					@ -54,15 +49,30 @@ describe('Environment', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('with environments', () => {
 | 
					    describe('with paginated environments', () => {
 | 
				
			||||||
      const environmentsResponseInterceptor = (request, next) => {
 | 
					      const environmentsResponseInterceptor = (request, next) => {
 | 
				
			||||||
        next(request.respondWith(JSON.stringify([environment]), {
 | 
					        next(request.respondWith(JSON.stringify({
 | 
				
			||||||
 | 
					          environments: [environment],
 | 
				
			||||||
 | 
					          stopped_count: 1,
 | 
				
			||||||
 | 
					          available_count: 0,
 | 
				
			||||||
 | 
					        }), {
 | 
				
			||||||
          status: 200,
 | 
					          status: 200,
 | 
				
			||||||
 | 
					          headers: {
 | 
				
			||||||
 | 
					            'X-nExt-pAge': '2',
 | 
				
			||||||
 | 
					            'x-page': '1',
 | 
				
			||||||
 | 
					            'X-Per-Page': '1',
 | 
				
			||||||
 | 
					            'X-Prev-Page': '',
 | 
				
			||||||
 | 
					            'X-TOTAL': '37',
 | 
				
			||||||
 | 
					            'X-Total-Pages': '2',
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      beforeEach(() => {
 | 
					      beforeEach(() => {
 | 
				
			||||||
        Vue.http.interceptors.push(environmentsResponseInterceptor);
 | 
					        Vue.http.interceptors.push(environmentsResponseInterceptor);
 | 
				
			||||||
 | 
					        component = new EnvironmentsComponent({
 | 
				
			||||||
 | 
					          el: document.querySelector('#environments-list-view'),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      afterEach(() => {
 | 
					      afterEach(() => {
 | 
				
			||||||
| 
						 | 
					@ -72,13 +82,6 @@ describe('Environment', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('should render a table with environments', (done) => {
 | 
					      it('should render a table with environments', (done) => {
 | 
				
			||||||
        component = new gl.environmentsList.EnvironmentsComponent({
 | 
					 | 
				
			||||||
          el: document.querySelector('#environments-list-view'),
 | 
					 | 
				
			||||||
          propsData: {
 | 
					 | 
				
			||||||
            store: gl.environmentsList.EnvironmentsStore.create(),
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        setTimeout(() => {
 | 
					        setTimeout(() => {
 | 
				
			||||||
          expect(
 | 
					          expect(
 | 
				
			||||||
            component.$el.querySelectorAll('table tbody tr').length,
 | 
					            component.$el.querySelectorAll('table tbody tr').length,
 | 
				
			||||||
| 
						 | 
					@ -86,6 +89,59 @@ describe('Environment', () => {
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        }, 0);
 | 
					        }, 0);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      describe('pagination', () => {
 | 
				
			||||||
 | 
					        it('should render pagination', (done) => {
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            expect(
 | 
				
			||||||
 | 
					              component.$el.querySelectorAll('.gl-pagination li').length,
 | 
				
			||||||
 | 
					            ).toEqual(5);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          }, 0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('should update url when no search params are present', (done) => {
 | 
				
			||||||
 | 
					          spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					            expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          }, 0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('should update url when page is already present', (done) => {
 | 
				
			||||||
 | 
					          spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					          window.history.pushState({}, null, '?page=1');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					            expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          }, 0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('should update url when page and scope are already present', (done) => {
 | 
				
			||||||
 | 
					          spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					          window.history.pushState({}, null, '?scope=all&page=1');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					            expect(gl.utils.visitUrl).toHaveBeenCalledWith('?scope=all&page=2');
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          }, 0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('should update url when page and scope are already present and page is first param', (done) => {
 | 
				
			||||||
 | 
					          spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					          window.history.pushState({}, null, '?page=1&scope=all');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					            expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2&scope=all');
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          }, 0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,11 +163,8 @@ describe('Environment', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should render empty state', (done) => {
 | 
					    it('should render empty state', (done) => {
 | 
				
			||||||
      component = new gl.environmentsList.EnvironmentsComponent({
 | 
					      component = new EnvironmentsComponent({
 | 
				
			||||||
        el: document.querySelector('#environments-list-view'),
 | 
					        el: document.querySelector('#environments-list-view'),
 | 
				
			||||||
        propsData: {
 | 
					 | 
				
			||||||
          store: gl.environmentsList.EnvironmentsStore.create(),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      setTimeout(() => {
 | 
					      setTimeout(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
require('~/environments/components/environment_stop');
 | 
					const StopComponent = require('~/environments/components/environment_stop');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Stop Component', () => {
 | 
					describe('Stop Component', () => {
 | 
				
			||||||
  preloadFixtures('static/environments/element.html.raw');
 | 
					  preloadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ describe('Stop Component', () => {
 | 
				
			||||||
    loadFixtures('static/environments/element.html.raw');
 | 
					    loadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    stopURL = '/stop';
 | 
					    stopURL = '/stop';
 | 
				
			||||||
    component = new window.gl.environmentsList.StopComponent({
 | 
					    component = new StopComponent({
 | 
				
			||||||
      el: document.querySelector('.test-dom-element'),
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
      propsData: {
 | 
					      propsData: {
 | 
				
			||||||
        stopUrl: stopURL,
 | 
					        stopUrl: stopURL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					const EnvironmentTable = require('~/environments/components/environments_table');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Environment item', () => {
 | 
				
			||||||
 | 
					  preloadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    loadFixtures('static/environments/element.html.raw');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('Should render a table', () => {
 | 
				
			||||||
 | 
					    const mockItem = {
 | 
				
			||||||
 | 
					      name: 'review',
 | 
				
			||||||
 | 
					      size: 3,
 | 
				
			||||||
 | 
					      isFolder: true,
 | 
				
			||||||
 | 
					      latest: {
 | 
				
			||||||
 | 
					        environment_path: 'url',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const component = new EnvironmentTable({
 | 
				
			||||||
 | 
					      el: document.querySelector('.test-dom-element'),
 | 
				
			||||||
 | 
					      propsData: {
 | 
				
			||||||
 | 
					        environments: [{ mockItem }],
 | 
				
			||||||
 | 
					        canCreateDeployment: false,
 | 
				
			||||||
 | 
					        canReadEnvironment: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(component.$el.tagName).toEqual('TABLE');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -1,70 +1,58 @@
 | 
				
			||||||
/* global environmentsList */
 | 
					const Store = require('~/environments/stores/environments_store');
 | 
				
			||||||
 | 
					const { environmentsList, serverData } = require('./mock_data');
 | 
				
			||||||
require('~/environments/stores/environments_store');
 | 
					 | 
				
			||||||
require('./mock_data');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
(() => {
 | 
					(() => {
 | 
				
			||||||
  describe('Store', () => {
 | 
					  describe('Store', () => {
 | 
				
			||||||
 | 
					    let store;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    beforeEach(() => {
 | 
					    beforeEach(() => {
 | 
				
			||||||
      gl.environmentsList.EnvironmentsStore.create();
 | 
					      store = new Store();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should start with a blank state', () => {
 | 
					    it('should start with a blank state', () => {
 | 
				
			||||||
      expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(0);
 | 
					      expect(store.state.environments.length).toEqual(0);
 | 
				
			||||||
      expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(0);
 | 
					      expect(store.state.stoppedCounter).toEqual(0);
 | 
				
			||||||
      expect(gl.environmentsList.EnvironmentsStore.state.availableCounter).toBe(0);
 | 
					      expect(store.state.availableCounter).toEqual(0);
 | 
				
			||||||
 | 
					      expect(store.state.paginationInformation).toEqual({});
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('store environments', () => {
 | 
					    it('should store environments', () => {
 | 
				
			||||||
      beforeEach(() => {
 | 
					      store.storeEnvironments(serverData);
 | 
				
			||||||
        gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList);
 | 
					      expect(store.state.environments.length).toEqual(serverData.length);
 | 
				
			||||||
      });
 | 
					      expect(store.state.environments[0]).toEqual(environmentsList[0]);
 | 
				
			||||||
 | 
					 | 
				
			||||||
      it('should count stopped environments and save the count in the state', () => {
 | 
					 | 
				
			||||||
        expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(1);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      it('should count available environments and save the count in the state', () => {
 | 
					 | 
				
			||||||
        expect(gl.environmentsList.EnvironmentsStore.state.availableCounter).toBe(3);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      it('should store environments with same environment_type as sibilings', () => {
 | 
					 | 
				
			||||||
        expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(3);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const parentFolder = gl.environmentsList.EnvironmentsStore.state.environments
 | 
					 | 
				
			||||||
        .filter(env => env.children && env.children.length > 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(parentFolder[0].children.length).toBe(2);
 | 
					 | 
				
			||||||
        expect(parentFolder[0].children[0].environment_type).toBe('review');
 | 
					 | 
				
			||||||
        expect(parentFolder[0].children[1].environment_type).toBe('review');
 | 
					 | 
				
			||||||
        expect(parentFolder[0].children[0].name).toBe('test-environment');
 | 
					 | 
				
			||||||
        expect(parentFolder[0].children[1].name).toBe('test-environment-1');
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      it('should sort the environments alphabetically', () => {
 | 
					 | 
				
			||||||
        const { environments } = gl.environmentsList.EnvironmentsStore.state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(environments[0].name).toBe('production');
 | 
					 | 
				
			||||||
        expect(environments[1].name).toBe('review');
 | 
					 | 
				
			||||||
        expect(environments[1].children[0].name).toBe('test-environment');
 | 
					 | 
				
			||||||
        expect(environments[1].children[1].name).toBe('test-environment-1');
 | 
					 | 
				
			||||||
        expect(environments[2].name).toBe('review_app');
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('toggleFolder', () => {
 | 
					    it('should store available count', () => {
 | 
				
			||||||
      beforeEach(() => {
 | 
					      store.storeAvailableCount(2);
 | 
				
			||||||
        gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList);
 | 
					      expect(store.state.availableCounter).toEqual(2);
 | 
				
			||||||
      });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('should toggle the open property for the given environment', () => {
 | 
					    it('should store stopped count', () => {
 | 
				
			||||||
        gl.environmentsList.EnvironmentsStore.toggleFolder('review');
 | 
					      store.storeStoppedCount(2);
 | 
				
			||||||
 | 
					      expect(store.state.stoppedCounter).toEqual(2);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const { environments } = gl.environmentsList.EnvironmentsStore.state;
 | 
					    it('should store pagination information', () => {
 | 
				
			||||||
        const environment = environments.filter(env => env['vue-isChildren'] === true && env.name === 'review');
 | 
					      const pagination = {
 | 
				
			||||||
 | 
					        'X-nExt-pAge': '2',
 | 
				
			||||||
 | 
					        'X-page': '1',
 | 
				
			||||||
 | 
					        'X-Per-Page': '1',
 | 
				
			||||||
 | 
					        'X-Prev-Page': '2',
 | 
				
			||||||
 | 
					        'X-TOTAL': '37',
 | 
				
			||||||
 | 
					        'X-Total-Pages': '2',
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        expect(environment[0].isOpen).toBe(true);
 | 
					      const expectedResult = {
 | 
				
			||||||
      });
 | 
					        perPage: 1,
 | 
				
			||||||
 | 
					        page: 1,
 | 
				
			||||||
 | 
					        total: 37,
 | 
				
			||||||
 | 
					        totalPages: 2,
 | 
				
			||||||
 | 
					        nextPage: 2,
 | 
				
			||||||
 | 
					        previousPage: 2,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      store.setPagination(pagination);
 | 
				
			||||||
 | 
					      expect(store.state.paginationInformation).toEqual(expectedResult);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,202 @@
 | 
				
			||||||
 | 
					const Vue = require('vue');
 | 
				
			||||||
 | 
					require('~/flash');
 | 
				
			||||||
 | 
					const EnvironmentsFolderViewComponent = require('~/environments/folder/environments_folder_view');
 | 
				
			||||||
 | 
					const { environmentsList } = require('../mock_data');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Environments Folder View', () => {
 | 
				
			||||||
 | 
					  preloadFixtures('static/environments/environments_folder_view.html.raw');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    loadFixtures('static/environments/environments_folder_view.html.raw');
 | 
				
			||||||
 | 
					    window.history.pushState({}, null, 'environments/folders/build');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('successfull request', () => {
 | 
				
			||||||
 | 
					    const environmentsResponseInterceptor = (request, next) => {
 | 
				
			||||||
 | 
					      next(request.respondWith(JSON.stringify({
 | 
				
			||||||
 | 
					        environments: environmentsList,
 | 
				
			||||||
 | 
					        stopped_count: 1,
 | 
				
			||||||
 | 
					        available_count: 0,
 | 
				
			||||||
 | 
					      }), {
 | 
				
			||||||
 | 
					        status: 200,
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					          'X-nExt-pAge': '2',
 | 
				
			||||||
 | 
					          'x-page': '1',
 | 
				
			||||||
 | 
					          'X-Per-Page': '1',
 | 
				
			||||||
 | 
					          'X-Prev-Page': '',
 | 
				
			||||||
 | 
					          'X-TOTAL': '37',
 | 
				
			||||||
 | 
					          'X-Total-Pages': '2',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    beforeEach(() => {
 | 
				
			||||||
 | 
					      Vue.http.interceptors.push(environmentsResponseInterceptor);
 | 
				
			||||||
 | 
					      component = new EnvironmentsFolderViewComponent({
 | 
				
			||||||
 | 
					        el: document.querySelector('#environments-folder-list-view'),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    afterEach(() => {
 | 
				
			||||||
 | 
					      Vue.http.interceptors = _.without(
 | 
				
			||||||
 | 
					        Vue.http.interceptors, environmentsResponseInterceptor,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render a table with environments', (done) => {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelectorAll('table tbody tr').length,
 | 
				
			||||||
 | 
					        ).toEqual(2);
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render available tab with count', (done) => {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-available-environments-folder-tab').textContent,
 | 
				
			||||||
 | 
					        ).toContain('Available');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-available-environments-folder-tab .js-available-environments-count').textContent,
 | 
				
			||||||
 | 
					        ).toContain('0');
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render stopped tab with count', (done) => {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-stopped-environments-folder-tab').textContent,
 | 
				
			||||||
 | 
					        ).toContain('Stopped');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-stopped-environments-folder-tab .js-stopped-environments-count').textContent,
 | 
				
			||||||
 | 
					        ).toContain('1');
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render parent folder name', (done) => {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-folder-name').textContent,
 | 
				
			||||||
 | 
					        ).toContain('Environments / build');
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    describe('pagination', () => {
 | 
				
			||||||
 | 
					      it('should render pagination', (done) => {
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          expect(
 | 
				
			||||||
 | 
					            component.$el.querySelectorAll('.gl-pagination li').length,
 | 
				
			||||||
 | 
					          ).toEqual(5);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, 0);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('should update url when no search params are present', (done) => {
 | 
				
			||||||
 | 
					        spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					          expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, 0);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('should update url when page is already present', (done) => {
 | 
				
			||||||
 | 
					        spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					        window.history.pushState({}, null, '?page=1');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					          expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, 0);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('should update url when page and scope are already present', (done) => {
 | 
				
			||||||
 | 
					        spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					        window.history.pushState({}, null, '?scope=all&page=1');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					          expect(gl.utils.visitUrl).toHaveBeenCalledWith('?scope=all&page=2');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, 0);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('should update url when page and scope are already present and page is first param', (done) => {
 | 
				
			||||||
 | 
					        spyOn(gl.utils, 'visitUrl');
 | 
				
			||||||
 | 
					        window.history.pushState({}, null, '?page=1&scope=all');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
 | 
				
			||||||
 | 
					          expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2&scope=all');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, 0);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('unsuccessfull request', () => {
 | 
				
			||||||
 | 
					    const environmentsErrorResponseInterceptor = (request, next) => {
 | 
				
			||||||
 | 
					      next(request.respondWith(JSON.stringify([]), {
 | 
				
			||||||
 | 
					        status: 500,
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    beforeEach(() => {
 | 
				
			||||||
 | 
					      Vue.http.interceptors.push(environmentsErrorResponseInterceptor);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    afterEach(() => {
 | 
				
			||||||
 | 
					      Vue.http.interceptors = _.without(
 | 
				
			||||||
 | 
					        Vue.http.interceptors, environmentsErrorResponseInterceptor,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not render a table', (done) => {
 | 
				
			||||||
 | 
					      component = new EnvironmentsFolderViewComponent({
 | 
				
			||||||
 | 
					        el: document.querySelector('#environments-folder-list-view'),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('table'),
 | 
				
			||||||
 | 
					        ).toBe(null);
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render available tab with count 0', (done) => {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-available-environments-folder-tab').textContent,
 | 
				
			||||||
 | 
					        ).toContain('Available');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-available-environments-folder-tab .js-available-environments-count').textContent,
 | 
				
			||||||
 | 
					        ).toContain('0');
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render stopped tab with count 0', (done) => {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-stopped-environments-folder-tab').textContent,
 | 
				
			||||||
 | 
					        ).toContain('Stopped');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(
 | 
				
			||||||
 | 
					          component.$el.querySelector('.js-stopped-environments-folder-tab .js-stopped-environments-count').textContent,
 | 
				
			||||||
 | 
					        ).toContain('0');
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -1,153 +1,92 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
const environmentsList = [
 | 
					const environmentsList = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    id: 31,
 | 
					    name: 'DEV',
 | 
				
			||||||
    name: 'production',
 | 
					    size: 1,
 | 
				
			||||||
 | 
					    id: 7,
 | 
				
			||||||
    state: 'available',
 | 
					    state: 'available',
 | 
				
			||||||
    external_url: 'https://www.gitlab.com',
 | 
					    external_url: null,
 | 
				
			||||||
    environment_type: null,
 | 
					    environment_type: null,
 | 
				
			||||||
    last_deployment: {
 | 
					    last_deployment: null,
 | 
				
			||||||
      id: 64,
 | 
					 | 
				
			||||||
      iid: 5,
 | 
					 | 
				
			||||||
      sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
 | 
					 | 
				
			||||||
      ref: {
 | 
					 | 
				
			||||||
        name: 'master',
 | 
					 | 
				
			||||||
        ref_url: 'http://localhost:3000/root/ci-folders/tree/master',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      tag: false,
 | 
					 | 
				
			||||||
      'last?': true,
 | 
					 | 
				
			||||||
      user: {
 | 
					 | 
				
			||||||
        name: 'Administrator',
 | 
					 | 
				
			||||||
        username: 'root',
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        state: 'active',
 | 
					 | 
				
			||||||
        avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
 | 
					 | 
				
			||||||
        web_url: 'http://localhost:3000/root',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      commit: {
 | 
					 | 
				
			||||||
        id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
 | 
					 | 
				
			||||||
        short_id: '500aabcb',
 | 
					 | 
				
			||||||
        title: 'Update .gitlab-ci.yml',
 | 
					 | 
				
			||||||
        author_name: 'Administrator',
 | 
					 | 
				
			||||||
        author_email: 'admin@example.com',
 | 
					 | 
				
			||||||
        created_at: '2016-11-07T18:28:13.000+00:00',
 | 
					 | 
				
			||||||
        message: 'Update .gitlab-ci.yml',
 | 
					 | 
				
			||||||
        author: {
 | 
					 | 
				
			||||||
          name: 'Administrator',
 | 
					 | 
				
			||||||
          username: 'root',
 | 
					 | 
				
			||||||
          id: 1,
 | 
					 | 
				
			||||||
          state: 'active',
 | 
					 | 
				
			||||||
          avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
 | 
					 | 
				
			||||||
          web_url: 'http://localhost:3000/root',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      deployable: {
 | 
					 | 
				
			||||||
        id: 1278,
 | 
					 | 
				
			||||||
        name: 'build',
 | 
					 | 
				
			||||||
        build_path: '/root/ci-folders/builds/1278',
 | 
					 | 
				
			||||||
        retry_path: '/root/ci-folders/builds/1278/retry',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      manual_actions: [],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    'stop_action?': true,
 | 
					 | 
				
			||||||
    environment_path: '/root/ci-folders/environments/31',
 | 
					 | 
				
			||||||
    created_at: '2016-11-07T11:11:16.525Z',
 | 
					 | 
				
			||||||
    updated_at: '2016-11-07T11:11:16.525Z',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    id: 32,
 | 
					 | 
				
			||||||
    name: 'review_app',
 | 
					 | 
				
			||||||
    state: 'stopped',
 | 
					 | 
				
			||||||
    external_url: 'https://www.gitlab.com',
 | 
					 | 
				
			||||||
    environment_type: null,
 | 
					 | 
				
			||||||
    last_deployment: {
 | 
					 | 
				
			||||||
      id: 64,
 | 
					 | 
				
			||||||
      iid: 5,
 | 
					 | 
				
			||||||
      sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
 | 
					 | 
				
			||||||
      ref: {
 | 
					 | 
				
			||||||
        name: 'master',
 | 
					 | 
				
			||||||
        ref_url: 'http://localhost:3000/root/ci-folders/tree/master',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      tag: false,
 | 
					 | 
				
			||||||
      'last?': true,
 | 
					 | 
				
			||||||
      user: {
 | 
					 | 
				
			||||||
        name: 'Administrator',
 | 
					 | 
				
			||||||
        username: 'root',
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        state: 'active',
 | 
					 | 
				
			||||||
        avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
 | 
					 | 
				
			||||||
        web_url: 'http://localhost:3000/root',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      commit: {
 | 
					 | 
				
			||||||
        id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
 | 
					 | 
				
			||||||
        short_id: '500aabcb',
 | 
					 | 
				
			||||||
        title: 'Update .gitlab-ci.yml',
 | 
					 | 
				
			||||||
        author_name: 'Administrator',
 | 
					 | 
				
			||||||
        author_email: 'admin@example.com',
 | 
					 | 
				
			||||||
        created_at: '2016-11-07T18:28:13.000+00:00',
 | 
					 | 
				
			||||||
        message: 'Update .gitlab-ci.yml',
 | 
					 | 
				
			||||||
        author: {
 | 
					 | 
				
			||||||
          name: 'Administrator',
 | 
					 | 
				
			||||||
          username: 'root',
 | 
					 | 
				
			||||||
          id: 1,
 | 
					 | 
				
			||||||
          state: 'active',
 | 
					 | 
				
			||||||
          avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
 | 
					 | 
				
			||||||
          web_url: 'http://localhost:3000/root',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      deployable: {
 | 
					 | 
				
			||||||
        id: 1278,
 | 
					 | 
				
			||||||
        name: 'build',
 | 
					 | 
				
			||||||
        build_path: '/root/ci-folders/builds/1278',
 | 
					 | 
				
			||||||
        retry_path: '/root/ci-folders/builds/1278/retry',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      manual_actions: [],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    'stop_action?': false,
 | 
					    'stop_action?': false,
 | 
				
			||||||
    environment_path: '/root/ci-folders/environments/31',
 | 
					    environment_path: '/root/review-app/environments/7',
 | 
				
			||||||
    created_at: '2016-11-07T11:11:16.525Z',
 | 
					    stop_path: '/root/review-app/environments/7/stop',
 | 
				
			||||||
    updated_at: '2016-11-07T11:11:16.525Z',
 | 
					    created_at: '2017-01-31T10:53:46.894Z',
 | 
				
			||||||
 | 
					    updated_at: '2017-01-31T10:53:46.894Z',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    id: 33,
 | 
					    folderName: 'build',
 | 
				
			||||||
    name: 'test-environment',
 | 
					    size: 5,
 | 
				
			||||||
 | 
					    id: 12,
 | 
				
			||||||
 | 
					    name: 'build/update-README',
 | 
				
			||||||
    state: 'available',
 | 
					    state: 'available',
 | 
				
			||||||
    environment_type: 'review',
 | 
					    external_url: null,
 | 
				
			||||||
 | 
					    environment_type: 'build',
 | 
				
			||||||
    last_deployment: null,
 | 
					    last_deployment: null,
 | 
				
			||||||
    'stop_action?': true,
 | 
					    'stop_action?': false,
 | 
				
			||||||
    environment_path: '/root/ci-folders/environments/31',
 | 
					    environment_path: '/root/review-app/environments/12',
 | 
				
			||||||
    created_at: '2016-11-07T11:11:16.525Z',
 | 
					    stop_path: '/root/review-app/environments/12/stop',
 | 
				
			||||||
    updated_at: '2016-11-07T11:11:16.525Z',
 | 
					    created_at: '2017-02-01T19:42:18.400Z',
 | 
				
			||||||
  },
 | 
					    updated_at: '2017-02-01T19:42:18.400Z',
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    id: 34,
 | 
					 | 
				
			||||||
    name: 'test-environment-1',
 | 
					 | 
				
			||||||
    state: 'available',
 | 
					 | 
				
			||||||
    environment_type: 'review',
 | 
					 | 
				
			||||||
    last_deployment: null,
 | 
					 | 
				
			||||||
    'stop_action?': true,
 | 
					 | 
				
			||||||
    environment_path: '/root/ci-folders/environments/31',
 | 
					 | 
				
			||||||
    created_at: '2016-11-07T11:11:16.525Z',
 | 
					 | 
				
			||||||
    updated_at: '2016-11-07T11:11:16.525Z',
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.environmentsList = environmentsList;
 | 
					const serverData = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    name: 'DEV',
 | 
				
			||||||
 | 
					    size: 1,
 | 
				
			||||||
 | 
					    latest: {
 | 
				
			||||||
 | 
					      id: 7,
 | 
				
			||||||
 | 
					      name: 'DEV',
 | 
				
			||||||
 | 
					      state: 'available',
 | 
				
			||||||
 | 
					      external_url: null,
 | 
				
			||||||
 | 
					      environment_type: null,
 | 
				
			||||||
 | 
					      last_deployment: null,
 | 
				
			||||||
 | 
					      'stop_action?': false,
 | 
				
			||||||
 | 
					      environment_path: '/root/review-app/environments/7',
 | 
				
			||||||
 | 
					      stop_path: '/root/review-app/environments/7/stop',
 | 
				
			||||||
 | 
					      created_at: '2017-01-31T10:53:46.894Z',
 | 
				
			||||||
 | 
					      updated_at: '2017-01-31T10:53:46.894Z',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    name: 'build',
 | 
				
			||||||
 | 
					    size: 5,
 | 
				
			||||||
 | 
					    latest: {
 | 
				
			||||||
 | 
					      id: 12,
 | 
				
			||||||
 | 
					      name: 'build/update-README',
 | 
				
			||||||
 | 
					      state: 'available',
 | 
				
			||||||
 | 
					      external_url: null,
 | 
				
			||||||
 | 
					      environment_type: 'build',
 | 
				
			||||||
 | 
					      last_deployment: null,
 | 
				
			||||||
 | 
					      'stop_action?': false,
 | 
				
			||||||
 | 
					      environment_path: '/root/review-app/environments/12',
 | 
				
			||||||
 | 
					      stop_path: '/root/review-app/environments/12/stop',
 | 
				
			||||||
 | 
					      created_at: '2017-02-01T19:42:18.400Z',
 | 
				
			||||||
 | 
					      updated_at: '2017-02-01T19:42:18.400Z',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const environment = {
 | 
					const environment = {
 | 
				
			||||||
  id: 4,
 | 
					  name: 'DEV',
 | 
				
			||||||
  name: 'production',
 | 
					  size: 1,
 | 
				
			||||||
  state: 'available',
 | 
					  latest: {
 | 
				
			||||||
  external_url: 'http://production.',
 | 
					    id: 7,
 | 
				
			||||||
  environment_type: null,
 | 
					    name: 'DEV',
 | 
				
			||||||
  last_deployment: {},
 | 
					    state: 'available',
 | 
				
			||||||
  'stop_action?': false,
 | 
					    external_url: null,
 | 
				
			||||||
  environment_path: '/root/review-app/environments/4',
 | 
					    environment_type: null,
 | 
				
			||||||
  stop_path: '/root/review-app/environments/4/stop',
 | 
					    last_deployment: null,
 | 
				
			||||||
  created_at: '2016-12-16T11:51:04.690Z',
 | 
					    'stop_action?': false,
 | 
				
			||||||
  updated_at: '2016-12-16T12:04:51.133Z',
 | 
					    environment_path: '/root/review-app/environments/7',
 | 
				
			||||||
 | 
					    stop_path: '/root/review-app/environments/7/stop',
 | 
				
			||||||
 | 
					    created_at: '2017-01-31T10:53:46.894Z',
 | 
				
			||||||
 | 
					    updated_at: '2017-01-31T10:53:46.894Z',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.environment = environment;
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  environmentsList,
 | 
				
			||||||
 | 
					  environment,
 | 
				
			||||||
 | 
					  serverData,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					%div
 | 
				
			||||||
 | 
					  #environments-folder-list-view{ data: { "can-create-deployment" => "true",
 | 
				
			||||||
 | 
					    "can-read-environment" => "true",
 | 
				
			||||||
 | 
					    "css-class" => "",
 | 
				
			||||||
 | 
					    "commit-icon-svg" => custom_icon("icon_commit"),
 | 
				
			||||||
 | 
					    "terminal-icon-svg" => custom_icon("icon_terminal"),
 | 
				
			||||||
 | 
					    "play-icon-svg" => custom_icon("icon_play") } }
 | 
				
			||||||
| 
						 | 
					@ -108,6 +108,30 @@ require('~/lib/utils/common_utils');
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    describe('gl.utils.parseIntPagination', () => {
 | 
				
			||||||
 | 
					      it('should parse to integers all string values and return pagination object', () => {
 | 
				
			||||||
 | 
					        const pagination = {
 | 
				
			||||||
 | 
					          'X-PER-PAGE': 10,
 | 
				
			||||||
 | 
					          'X-PAGE': 2,
 | 
				
			||||||
 | 
					          'X-TOTAL': 30,
 | 
				
			||||||
 | 
					          'X-TOTAL-PAGES': 3,
 | 
				
			||||||
 | 
					          'X-NEXT-PAGE': 3,
 | 
				
			||||||
 | 
					          'X-PREV-PAGE': 1,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const expectedPagination = {
 | 
				
			||||||
 | 
					          perPage: 10,
 | 
				
			||||||
 | 
					          page: 2,
 | 
				
			||||||
 | 
					          total: 30,
 | 
				
			||||||
 | 
					          totalPages: 3,
 | 
				
			||||||
 | 
					          nextPage: 3,
 | 
				
			||||||
 | 
					          previousPage: 1,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(gl.utils.parseIntPagination(pagination)).toEqual(expectedPagination);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('gl.utils.isMetaClick', () => {
 | 
					    describe('gl.utils.isMetaClick', () => {
 | 
				
			||||||
      it('should identify meta click on Windows/Linux', () => {
 | 
					      it('should identify meta click on Windows/Linux', () => {
 | 
				
			||||||
        const e = {
 | 
					        const e = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ describe('Pagination component', () => {
 | 
				
			||||||
    component.changePage({ target: { innerText: '1' } });
 | 
					    component.changePage({ target: { innerText: '1' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(changeChanges.one).toEqual(1);
 | 
					    expect(changeChanges.one).toEqual(1);
 | 
				
			||||||
    expect(changeChanges.two).toEqual('all');
 | 
					    expect(changeChanges.two).toEqual(null);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should go to the previous page', () => {
 | 
					  it('should go to the previous page', () => {
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@ describe('Pagination component', () => {
 | 
				
			||||||
    component.changePage({ target: { innerText: 'Prev' } });
 | 
					    component.changePage({ target: { innerText: 'Prev' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(changeChanges.one).toEqual(1);
 | 
					    expect(changeChanges.one).toEqual(1);
 | 
				
			||||||
    expect(changeChanges.two).toEqual('all');
 | 
					    expect(changeChanges.two).toEqual(null);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should go to the next page', () => {
 | 
					  it('should go to the next page', () => {
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ describe('Pagination component', () => {
 | 
				
			||||||
    component.changePage({ target: { innerText: 'Next' } });
 | 
					    component.changePage({ target: { innerText: 'Next' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(changeChanges.one).toEqual(5);
 | 
					    expect(changeChanges.one).toEqual(5);
 | 
				
			||||||
    expect(changeChanges.two).toEqual('all');
 | 
					    expect(changeChanges.two).toEqual(null);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should go to the last page', () => {
 | 
					  it('should go to the last page', () => {
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,7 @@ describe('Pagination component', () => {
 | 
				
			||||||
    component.changePage({ target: { innerText: 'Last >>' } });
 | 
					    component.changePage({ target: { innerText: 'Last >>' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(changeChanges.one).toEqual(10);
 | 
					    expect(changeChanges.one).toEqual(10);
 | 
				
			||||||
    expect(changeChanges.two).toEqual('all');
 | 
					    expect(changeChanges.two).toEqual(null);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should go to the first page', () => {
 | 
					  it('should go to the first page', () => {
 | 
				
			||||||
| 
						 | 
					@ -118,7 +118,7 @@ describe('Pagination component', () => {
 | 
				
			||||||
    component.changePage({ target: { innerText: '<< First' } });
 | 
					    component.changePage({ target: { innerText: '<< First' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(changeChanges.one).toEqual(1);
 | 
					    expect(changeChanges.one).toEqual(1);
 | 
				
			||||||
    expect(changeChanges.two).toEqual('all');
 | 
					    expect(changeChanges.two).toEqual(null);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should do nothing', () => {
 | 
					  it('should do nothing', () => {
 | 
				
			||||||
| 
						 | 
					@ -139,7 +139,7 @@ describe('Pagination component', () => {
 | 
				
			||||||
    component.changePage({ target: { innerText: '...' } });
 | 
					    component.changePage({ target: { innerText: '...' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(changeChanges.one).toEqual(1);
 | 
					    expect(changeChanges.one).toEqual(1);
 | 
				
			||||||
    expect(changeChanges.two).toEqual('all');
 | 
					    expect(changeChanges.two).toEqual(null);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,6 +181,17 @@ describe EnvironmentSerializer do
 | 
				
			||||||
          expect(subject.first[:name]).to eq 'production'
 | 
					          expect(subject.first[:name]).to eq 'production'
 | 
				
			||||||
          expect(subject.second[:name]).to eq 'staging'
 | 
					          expect(subject.second[:name]).to eq 'staging'
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'appends correct total page count header' do
 | 
				
			||||||
 | 
					          expect(subject).not_to be_empty
 | 
				
			||||||
 | 
					          expect(response).to have_received(:[]=).with('X-Total', '3')
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'appends correct page count headers' do
 | 
				
			||||||
 | 
					          expect(subject).not_to be_empty
 | 
				
			||||||
 | 
					          expect(response).to have_received(:[]=).with('X-Total-Pages', '2')
 | 
				
			||||||
 | 
					          expect(response).to have_received(:[]=).with('X-Per-Page', '2')
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue