Commit ed072645 authored by Filipa Lacerda's avatar Filipa Lacerda

Adds props validation

Improves documentation

Adds tests

Fix prop validation for objects

Finish tests for environment item

Adds tests for toggle folder function

Environment tests

Adds tests
parent bebbf12a
...@@ -11,6 +11,7 @@ class BoardService { ...@@ -11,6 +11,7 @@ class BoardService {
this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {}); this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {});
Vue.http.interceptors.push((request, next) => { Vue.http.interceptors.push((request, next) => {
debugger;
request.headers['X-CSRF-Token'] = $.rails.csrfToken(); request.headers['X-CSRF-Token'] = $.rails.csrfToken();
next(); next();
}); });
......
...@@ -23,8 +23,14 @@ $(() => { ...@@ -23,8 +23,14 @@ $(() => {
return fn(item); return fn(item);
}).filter(Boolean); }).filter(Boolean);
window.gl.environmentsList.EnvironmentsComponent = Vue.extend({ window.gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', {
props: ['store'], props: {
store: {
type: Object,
required: true,
default: () => ({}),
},
},
components: { components: {
'environment-item': window.gl.environmentsList.EnvironmentItem, 'environment-item': window.gl.environmentsList.EnvironmentItem,
...@@ -43,9 +49,8 @@ $(() => { ...@@ -43,9 +49,8 @@ $(() => {
projectStoppedEnvironmentsPath: environmentsListApp.dataset.projectStoppedEnvironmentsPath, projectStoppedEnvironmentsPath: environmentsListApp.dataset.projectStoppedEnvironmentsPath,
newEnvironmentPath: environmentsListApp.dataset.newEnvironmentPath, newEnvironmentPath: environmentsListApp.dataset.newEnvironmentPath,
helpPagePath: environmentsListApp.dataset.helpPagePath, helpPagePath: environmentsListApp.dataset.helpPagePath,
loading: true,
visibility: 'available', visibility: 'available',
isLoading: this.loading, isLoading: false,
}; };
}, },
...@@ -65,6 +70,10 @@ $(() => { ...@@ -65,6 +70,10 @@ $(() => {
canCreateDeploymentParsed() { canCreateDeploymentParsed() {
return this.$options.convertPermissionToBoolean(this.canCreateDeployment); return this.$options.convertPermissionToBoolean(this.canCreateDeployment);
}, },
canCreateEnvironmentParsed() {
return this.$options.convertPermissionToBoolean(this.canCreateEnvironment);
},
}, },
created() { created() {
...@@ -74,6 +83,15 @@ $(() => { ...@@ -74,6 +83,15 @@ $(() => {
if (scope) { if (scope) {
this.visibility = scope; this.visibility = scope;
} }
this.isLoading = true;
return window.gl.environmentsService.all()
.then(resp => resp.json())
.then((json) => {
this.store.storeEnvironments(json);
this.isLoading = false;
});
}, },
/** /**
...@@ -81,12 +99,7 @@ $(() => { ...@@ -81,12 +99,7 @@ $(() => {
* Toggles loading property. * Toggles loading property.
*/ */
mounted() { mounted() {
window.gl.environmentsService.all()
.then(resp => resp.json())
.then((json) => {
this.store.storeEnvironments(json);
this.loading = false;
});
}, },
/** /**
...@@ -143,21 +156,21 @@ $(() => { ...@@ -143,21 +156,21 @@ $(() => {
</a> </a>
</li> </li>
</ul> </ul>
<div v-if="canCreateEnvironment && !loading" class="nav-controls"> <div v-if="canCreateEnvironmentParsed && !isLoading" class="nav-controls">
<a :href="newEnvironmentPath" class="btn btn-create"> <a :href="newEnvironmentPath" class="btn btn-create">
New envrionment New environment
</a> </a>
</div> </div>
</div> </div>
<div class="environments-container"> <div class="environments-container">
<div class="environments-list-loading text-center" v-if="loading"> <div class="environments-list-loading text-center" v-if="isLoading">
<i class="fa fa-spinner spin"></i> <i class="fa fa-spinner spin"></i>
</div> </div>
<div <div
class="blank-state blank-state-no-icon" class="blank-state blank-state-no-icon"
v-if="!loading && state.environments.length === 0"> v-if="!isLoading && state.environments.length === 0">
<h2 class="blank-state-title"> <h2 class="blank-state-title">
You don't have any environments right now. You don't have any environments right now.
</h2> </h2>
...@@ -170,7 +183,7 @@ $(() => { ...@@ -170,7 +183,7 @@ $(() => {
Read more about environments Read more about environments
</a> </a>
<a <a
v-if="canCreateEnvironment" v-if="canCreateEnvironmentParsed"
:href="newEnvironmentPath" :href="newEnvironmentPath"
class="btn btn-create"> class="btn btn-create">
New Environment New Environment
...@@ -180,7 +193,7 @@ $(() => { ...@@ -180,7 +193,7 @@ $(() => {
<div <div
class="table-holder" class="table-holder"
v-if="!loading && state.environments.length > 0"> v-if="!isLoading && state.environments.length > 0">
<table class="table ci-table environments"> <table class="table ci-table environments">
<thead> <thead>
<tr> <tr>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
}, },
template: ` template: `
<a class="btn external_url" :href="external_url" :target="_blank"> <a class="btn external_url" :href="external_url" target="_blank">
<i class="fa fa-external-link"></i> <i class="fa fa-external-link"></i>
</a> </a>
`, `,
......
...@@ -8,7 +8,10 @@ class EnvironmentsService { ...@@ -8,7 +8,10 @@ class EnvironmentsService {
this.environments = Vue.resource(root); this.environments = Vue.resource(root);
Vue.http.interceptors.push((request, next) => { Vue.http.interceptors.push((request, next) => {
request.headers['X-CSRF-Token'] = $.rails.csrfToken(); // needed in order to not break the tests.
if ($.rails) {
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
}
next(); next();
}); });
} }
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
ref: { ref: {
type: Object, type: Object,
required: false, required: false,
default: () => {}, default: () => ({}),
}, },
/** /**
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
author: { author: {
type: Object, type: Object,
required: false, required: false,
default: () => {}, default: () => ({}),
}, },
}, },
......
...@@ -106,6 +106,9 @@ ActiveRecord::Schema.define(version: 20161109150329) do ...@@ -106,6 +106,9 @@ ActiveRecord::Schema.define(version: 20161109150329) do
t.integer "housekeeping_incremental_repack_period", default: 10, null: false t.integer "housekeeping_incremental_repack_period", default: 10, null: false
t.integer "housekeeping_full_repack_period", default: 50, null: false t.integer "housekeeping_full_repack_period", default: 50, null: false
t.integer "housekeeping_gc_period", default: 200, null: false t.integer "housekeeping_gc_period", default: 200, null: false
t.boolean "sidekiq_throttling_enabled", default: false
t.string "sidekiq_throttling_queues"
t.decimal "sidekiq_throttling_factor"
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
......
require 'spec_helper' require 'spec_helper'
feature 'Environments', feature: true do feature 'Environments', feature: true, js:true do
given(:project) { create(:empty_project) } given(:project) { create(:empty_project) }
given(:user) { create(:user) } given(:user) { create(:user) }
given(:role) { :developer } given(:role) { :developer }
...@@ -59,6 +59,7 @@ feature 'Environments', feature: true do ...@@ -59,6 +59,7 @@ feature 'Environments', feature: true do
given(:deployment) { create(:deployment, environment: environment) } given(:deployment) { create(:deployment, environment: environment) }
scenario 'does show deployment SHA' do scenario 'does show deployment SHA' do
puts page.body
expect(page).to have_link(deployment.short_sha) expect(page).to have_link(deployment.short_sha)
end end
......
...@@ -9,6 +9,7 @@ describe('Environment item', () => { ...@@ -9,6 +9,7 @@ describe('Environment item', () => {
describe('When item is folder', () => { describe('When item is folder', () => {
let mockItem; let mockItem;
let component;
beforeEach(() => { beforeEach(() => {
mockItem = { mockItem = {
...@@ -34,61 +35,31 @@ describe('Environment item', () => { ...@@ -34,61 +35,31 @@ describe('Environment item', () => {
}, },
], ],
}; };
});
it('Should render clickable folder icon and name', () => { component = new window.gl.environmentsList.EnvironmentItem({
const component = new window.gl.environmentsList.EnvironmentItem({
el: document.querySelector('tr#environment-row'), el: document.querySelector('tr#environment-row'),
propsData: { propsData: {
model: mockItem, model: mockItem,
toggleRow: () => {}, toggleRow: () => {},
'can-create-deployment': false, canCreateDeployment: false,
'can-read-environment': true, canReadEnvironment: true,
}, },
}); });
});
it('Should render folder icon and name', () => {
expect(component.$el.querySelector('.folder-name').textContent).toContain(mockItem.name); expect(component.$el.querySelector('.folder-name').textContent).toContain(mockItem.name);
expect(component.$el.querySelector('.folder-icon')).toBeDefined(); expect(component.$el.querySelector('.folder-icon')).toBeDefined();
}); });
it('Should render the number of children in a badge', () => { it('Should render the number of children in a badge', () => {
const component = new window.gl.environmentsList.EnvironmentItem({
el: document.querySelector('tr#environment-row'),
propsData: {
model: mockItem,
toggleRow: () => {},
'can-create-deployment': false,
'can-read-environment': true,
},
});
expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.children.length); expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.children.length);
}); });
describe('when clicked', () => {
it('Should call the given prop', () => {
const component = new window.gl.environmentsList.EnvironmentItem({
el: document.querySelector('tr#environment-row'),
propsData: {
model: mockItem,
toggleRow: () => {
console.log('here!');
},
'can-create-deployment': false,
'can-read-environment': true,
},
});
spyOn(component.$options.propsData, 'toggleRow');
component.$el.querySelector('.folder-name').click();
expect(component.$options.propsData.toggleRow).toHaveBeenCalled();
});
});
}); });
describe('when item is not folder', () => { describe('when item is not folder', () => {
let environment; let environment;
let component;
beforeEach(() => { beforeEach(() => {
environment = { environment = {
...@@ -151,104 +122,93 @@ describe('Environment item', () => { ...@@ -151,104 +122,93 @@ describe('Environment item', () => {
created_at: '2016-11-07T11:11:16.525Z', created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-10T15:55:58.778Z', updated_at: '2016-11-10T15:55:58.778Z',
}; };
});
it('should render environment name', () => { component = new window.gl.environmentsList.EnvironmentItem({
const component = new window.gl.environmentsList.EnvironmentItem({
el: document.querySelector('tr#environment-row'), el: document.querySelector('tr#environment-row'),
propsData: { propsData: {
model: environment, model: environment,
toggleRow: () => {}, toggleRow: () => {},
'can-create-deployment': false, canCreateDeployment: true,
'can-read-environment': true, canReadEnvironment: true,
}, },
}); });
});
debugger; it('should render environment name', () => {
expect(component.$el.querySelector('.environment-name').textContent).toEqual(environment.name);
}); });
describe('With deployment', () => { describe('With deployment', () => {
it('should render deployment internal id', () => { it('should render deployment internal id', () => {
expect(
component.$el.querySelector('.deployment-column span').textContent
).toContain(environment.last_deployment.iid);
}); expect(
component.$el.querySelector('.deployment-column span').textContent
it('should link to deployment', () => { ).toContain('#');
}); });
describe('With user information', () => { describe('With user information', () => {
it('should render user avatar with link to profile', () => { it('should render user avatar with link to profile', () => {
expect(
component.$el.querySelector('.js-deploy-user-container').getAttribute('href')
).toEqual(environment.last_deployment.user.web_url);
}); });
}); });
describe('With build url', () => { describe('With build url', () => {
it('Should link to build url provided', () => { it('Should link to build url provided', () => {
expect(
component.$el.querySelector('.build-link').getAttribute('href')
).toEqual(environment.last_deployment.deployable.build_url);
}); });
it('Should render deployable name and id', () => { it('Should render deployable name and id', () => {
expect(
component.$el.querySelector('.build-link').getAttribute('href')
).toEqual(environment.last_deployment.deployable.build_url);
}); });
}); });
describe('With commit information', () => { describe('With commit information', () => {
it('should render commit component', () => {}); it('should render commit component', () => {
}); expect(
component.$el.querySelector('.js-commit-component')
it('Should render timeago created date', () => { ).toBeDefined();
});
});
});
describe('Without deployment', () => {
it('should render no deployments information', () => {
}); });
}); });
describe('With manual actions', () => { describe('With manual actions', () => {
describe('With create deployment permission', () => { it('Should render actions component', () => {
it('Should render actions component', () => { expect(
component.$el.querySelector('.js-manual-actions-container')
}); ).toBeDefined();
});
describe('Without create deployment permission', () => {
it('should not render actions component', () => {
});
}); });
}); });
describe('With external URL', () => { describe('With external URL', () => {
it('should render external url component', () => { it('should render external url component', () => {
expect(
component.$el.querySelector('.js-external-url-container')
).toBeDefined();
}); });
}); });
describe('With stop action', () => { describe('With stop action', () => {
describe('With create deployment permission', () => { it('Should render stop action component', () => {
it('Should render stop action component', () => { expect(
component.$el.querySelector('.js-stop-component-container')
}); ).toBeDefined();
});
describe('Without create deployment permission', () => {
it('should not render stop action component', () => {
});
}); });
}); });
describe('With retry action', () => { describe('With retry action', () => {
describe('With create deployment permission', () => { it('Should render rollback component', () => {
it('Should render rollback component', () => { expect(
component.$el.querySelector('.js-rollback-component-container')
}); ).toBeDefined();
});
describe('Without create deployment permission', () => {
it('should not render rollback component', () => {
});
}); });
}); });
}); });
......
//= require vue
//= require environments/stores/environments_store
//= require environments/components/environment
/* globals environmentsList */
describe('Environments', () => {
fixture.preload('environments/environments.html');
fixture.preload('environments/environments_no_permission.html');
let Store;
let component;
beforeEach(() => {
Store = window.gl.environmentsList.EnvironmentsStore;
});
describe('While loading', () => {
beforeEach(() => {
fixture.load('environments/environments.html');
component = new window.gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: Store.create(),
},
});
});
it('Should render two tabs', () => {
expect(component.$el.querySelectorAll('ul li').length).toEqual(2);
});
it('Should render bagdes with zeros in both tabs indicating the number of available environments', () => {
expect(
component.$el.querySelector('.js-available-environments-count').textContent
).toContain('0');
expect(
component.$el.querySelector('.js-stopped-environments-count').textContent
).toContain('0');
});
it('Should render loading icon', () => {
expect(
component.$el.querySelector('environments-list-loading')
).toBeDefined();
});
});
describe('Without environments', () => {
beforeEach(() => {
fixture.load('environments/environments.html');
spyOn(component, 'ready').and.callFake(() => {
return {
then: callback => callback([]),
json: () => ({ then: cb => cb([]) }),
};
});
component = new window.gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: Store.create(),
},
});
});
it('Should render two tabs', () => {
expect(component.$el.querySelectorAll('ul li').length).toEqual(2);
});
it('Should render bagdes with zeros in both tabs indicating the number of available environments', () => {
expect(
component.$el.querySelector('.js-available-environments-count').textContent
).toContain('0');
expect(
component.$el.querySelector('.js-stopped-environments-count').textContent
).toContain('0');
});
it('Should render blank state information', () => {
expect(
component.$el.querySelector('.blank-state-title').textContent
).toEqual('You don\'t have any environments right now.');
expect(
component.$el.querySelector('.blank-state-text').textContent
).toEqual('Environments are places where code gets deployed, such as staging or production.');
});
it('Should render the provided help url', () => {
expect(
component.$el.querySelector('.blank-state a').getAttribute('href')
).toEqual(component.$data.helpPagePath);
});
describe('With create permission', () => {
it('Should render new environment button', () => {
expect(
component.$el.querySelector('a.btn-create').getAttribute('href')
).toEqual(component.$data.newEnvironmentPath);
expect(
component.$el.querySelector('a.btn-create').textContent
).toEqual('New environment');
});
});
describe('Without create permission', () => {
beforeEach('Load fixture without permission', () => {
fixture.load('environments/environments_no_permission.html');
component = new window.gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: Store.create(),
},
});
});
it('Should not render new environment button', () => {
});
});
});
describe('With environments', () => {
describe('Tabs behavior', () => {
it('Should render two tabs', () => {
});
it('Should render badges with the correct count', () => {
});
describe('When clicking in the available tab', () => {
it('Should make Available tab active', () => {
});
it('Should make visible only available environments', () => {
});
});
describe('When clicking in the stopped tab', () => {
it('Should make Stopped tab active', () => {
});
it('Should make visible only stopped environments', () => {
});
});
});
describe('With create permissions', () => {
it('Should render new environment button', () => {
});
});
describe('Without create permissions', () => {
it('Should not render the new environment button', () => {
});
});
it('Should render a table', () => {
});
it('Should render table pagination', () => {
});
});
});
//= require vue //= require vue
//= require environments/stores/environmnets_store //= require environments/stores/environments_store
//= require ./mock_data //= require ./mock_data
/* globals environmentsList */ /* globals environmentsList */
(() => { (() => {
...@@ -50,5 +50,20 @@ ...@@ -50,5 +50,20 @@
expect(environments[2].name).toBe('review_app'); expect(environments[2].name).toBe('review_app');
}); });
}); });
describe('toggleFolder', () => {
beforeEach(() => {
gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList);
});
it('should toggle the open property for the given environment', () => {
gl.environmentsList.EnvironmentsStore.toggleFolder('review');
const { environments } = gl.environmentsList.EnvironmentsStore.state;
const environment = environments.filter(env => env['vue-isChildren'] === true && env.name === 'review');
expect(environment[0].isOpen).toBe(true);
});
});
}); });
})(); })();
%div %div
#environments-list-view{ data: { environments_data: "https://gitlab.com/foo/environments", #environments-list-view{ data: { environments_data: "https://gitlab.com/foo/environments",
"can-create-deployment" => "true", "can-create-deployment" => "true",
"can-read-environment" => "true", "can-read-environment" => "true",
"can-create-environmnet" => "true", "can-create-environment" => "true",
"project-environments-path" => "https://gitlab.com/foo/environments", "project-environments-path" => "https://gitlab.com/foo/environments",
"project-stopped-environments-path" => "https://gitlab.com/foo/environments?scope=stopped", "project-stopped-environments-path" => "https://gitlab.com/foo/environments?scope=stopped",
"new-environment-path" => "https://gitlab.com/foo/environments/new", "new-environment-path" => "https://gitlab.com/foo/environments/new",
"help-page-path" => "https://gitlab.com/help_page"}} "help-page-path" => "https://gitlab.com/help_page"}}
\ No newline at end of file
%div
#environments-list-view{ data: { environments_data: "https://gitlab.com/foo/environments",
"can-create-deployment" => "false",
"can-read-environment" => "true",
"can-create-environmnet" => "false",
"project-environments-path" => "https://gitlab.com/foo/environments",
"project-stopped-environments-path" => "https://gitlab.com/foo/environments?scope=stopped",
"new-environment-path" => "https://gitlab.com/foo/environments/new",
"help-page-path" => "https://gitlab.com/help_page"}}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment