Skip to content
Snippets Groups Projects
Commit 446b59dd authored by Filipa Lacerda's avatar Filipa Lacerda
Browse files

Adds tests to new empty and error states

parent 2c85a204
No related branches found
No related tags found
No related merge requests found
Showing
with 460 additions and 49 deletions
Loading
Loading
@@ -5,6 +5,7 @@ import PipelinesTableComponent from '../../vue_shared/components/pipelines_table
import PipelinesService from '../../vue_pipelines_index/services/pipelines_service';
import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store';
import eventHub from '../../vue_pipelines_index/event_hub';
import EmptyState from '../../vue_pipelines_index/components/empty_state';
import ErrorState from '../../vue_pipelines_index/components/error_state';
import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor';
Loading
Loading
@@ -24,6 +25,7 @@ export default Vue.component('pipelines-table', {
components: {
'pipelines-table-component': PipelinesTableComponent,
'error-state': ErrorState,
'empty-state': EmptyState,
},
 
/**
Loading
Loading
@@ -38,6 +40,7 @@ export default Vue.component('pipelines-table', {
 
return {
endpoint: pipelinesTableData.endpoint,
helpPagePath: pipelinesTableData.helpPagePath,
store,
state: store.state,
isLoading: false,
Loading
Loading
@@ -49,6 +52,10 @@ export default Vue.component('pipelines-table', {
shouldRenderErrorState() {
return this.hasError && !this.pageRequest;
},
shouldRenderEmptyState() {
return !this.state.pipelines.length && !this.pageRequest;
},
},
 
/**
Loading
Loading
@@ -102,6 +109,8 @@ export default Vue.component('pipelines-table', {
<i class="fa fa-spinner fa-spin"></i>
</div>
 
<empty-state v-if="shouldRenderEmptyState" :helpPagePath="helpPagePath" />
<error-state v-if="shouldRenderErrorState" />
 
<div class="table-holder pipelines"
Loading
Loading
Loading
Loading
@@ -15,7 +15,7 @@ export default {
},
 
template: `
<div class="row empty-state">
<div class="row empty-state js-pipelines-empty-state">
<div class="col-xs-12 pull-right">
<div class="svg-content">
${pipelinesEmptyStateSVG}
Loading
Loading
@@ -28,10 +28,10 @@ export default {
<p>
Continous Integration can help catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver code to your product environment.
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
</p>
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
</div>
</div>
</div>
Loading
Loading
Loading
Loading
@@ -8,18 +8,18 @@ export default {
},
 
template: `
<div class="row empty-state">
<div class="col-xs-12 pull-right">
<div class="svg-content">
${pipelinesErrorStateSVG}
<div class="row empty-state js-pipelines-error-state">
<div class="col-xs-12 pull-right">
<div class="svg-content">
${pipelinesErrorStateSVG}
</div>
</div>
</div>
 
<div class="col-xs-12 center">
<div class="text-content">
<h4>The API failed to fetch the pipelines.</h4>
<div class="col-xs-12 center">
<div class="text-content">
<h4>The API failed to fetch the pipelines.</h4>
</div>
</div>
</div>
</div>
`,
};
Loading
Loading
@@ -18,7 +18,9 @@ export default {
 
template: `
<ul class="nav-links">
<li :class="{ 'active': scope === 'all'}">
<li
class="js-pipelines-tab-all"
:class="{ 'active': scope === 'all'}">
<a :href="paths.allPath">
All
<span class="badge js-totalbuilds-count">
Loading
Loading
Loading
Loading
@@ -32,7 +32,19 @@ export default {
const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
 
return {
...pipelinesData,
endpoint: pipelinesData.endpoint,
cssClass: pipelinesData.cssClass,
helpPagePath: pipelinesData.helpPagePath,
newPipelinePath: pipelinesData.newPipelinePath,
canCreatePipeline: pipelinesData.canCreatePipeline,
allPath: pipelinesData.allPath,
pendingPath: pipelinesData.pendingPath,
runningPath: pipelinesData.runningPath,
finishedPath: pipelinesData.finishedPath,
branchesPath: pipelinesData.branchesPath,
tagsPath: pipelinesData.tagsPath,
hasCi: pipelinesData.hasCi,
ciLintPath: pipelinesData.ciLintPath,
state: this.store.state,
apiScope: 'all',
pagenum: 1,
Loading
Loading
@@ -172,8 +184,7 @@ export default {
 
template: `
<div
:class="cssClass"
class="pipelines">
:class="cssClass">
 
<div
class="top-area"
Loading
Loading
@@ -191,37 +202,40 @@ export default {
:canCreatePipeline="canCreatePipelineParsed " />
</div>
 
<div
class="realtime-loading"
v-if="pageRequest">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
<div class="content-list pipelines">
 
<empty-state v-if="shouldRenderEmptyState" :helpPagePath="helpPagePath" />
<div
class="realtime-loading"
v-if="pageRequest">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
 
<error-state v-if="shouldRenderErrorState" />
<empty-state v-if="shouldRenderEmptyState" :helpPagePath="helpPagePath" />
 
<div
class="blank-state blank-state-no-icon"
v-if="shouldRenderNoPipelinesMessage">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
</div>
<error-state v-if="shouldRenderErrorState" />
 
<div
class="table-holder"
v-if="shouldRenderTable">
<div
class="blank-state blank-state-no-icon"
v-if="shouldRenderNoPipelinesMessage">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
</div>
 
<pipelines-table-component
:pipelines="state.pipelines"
:service="service"/>
</div>
<div
class="table-holder"
v-if="shouldRenderTable">
 
<gl-pagination
v-if="shouldRenderPagination"
:pagenum="pagenum"
:change="change"
:count="state.count.all"
:pageInfo="state.pageInfo"/>
<pipelines-table-component
:pipelines="state.pipelines"
:service="service"/>
</div>
<gl-pagination
v-if="shouldRenderPagination"
:pagenum="pagenum"
:change="change"
:count="state.count.all"
:pageInfo="state.pageInfo"/>
</div>
</div>
`,
};
- disable_initialization = local_assigns.fetch(:disable_initialization, false)
#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization,
endpoint: endpoint,
"help-page-path" => help_page_path('ci/quick_start/README')
} }
 
- content_for :page_specific_javascripts do
Loading
Loading
Loading
Loading
@@ -2,10 +2,6 @@
- page_title "Pipelines"
= render "projects/pipelines/head"
 
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag("common_vue")
= page_specific_javascript_bundle_tag("vue_pipelines")
#pipelines-list-vue{ data: { endpoint: namespace_project_pipelines_path(@project.namespace, @project, format: :json),
"css-class" => container_class,
"help-page-path" => help_page_path('ci/quick_start/README'),
Loading
Loading
@@ -19,3 +15,6 @@
"tags-path" => project_pipelines_path(@project, scope: :tags),
"has-ci" => @repository.gitlab_ci_yml,
"ci-lint-path" => ci_lint_path } }
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('vue_pipelines')
Loading
Loading
@@ -442,7 +442,7 @@ describe 'Pipelines', :feature, :js do
context 'when project is public' do
let(:project) { create(:project, :public) }
 
it { expect(page).to have_content 'No pipelines to show' }
it { expect(page).to have_content 'Build with confidence' }
it { expect(page).to have_http_status(:success) }
end
 
Loading
Loading
Loading
Loading
@@ -33,7 +33,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
 
setTimeout(() => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
expect(component.$el.querySelector('.js-pipelines-empty-state')).toBeDefined();
done();
}, 1);
});
Loading
Loading
@@ -92,7 +92,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
 
setTimeout(() => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
done();
}, 0);
});
Loading
Loading
%div
#pipelines-list-vue{ data: { endpoint: 'foo',
"css-class" => 'foo',
"help-page-path" => 'foo',
"new-pipeline-path" => 'foo',
"can-create-pipeline" => 'true',
"all-path" => 'foo',
"pending-path" => 'foo',
"running-path" => 'foo',
"finished-path" => 'foo',
"branches-path" => 'foo',
"tags-path" => 'foo',
"has-ci" => 'foo',
"ci-lint-path" => 'foo' } }
import Vue from 'vue';
import emptyStateComp from '~/vue_pipelines_index/components/empty_state';
describe('Pipelines Empty State', () => {
let component;
let EmptyStateComponent;
beforeEach(() => {
EmptyStateComponent = Vue.extend(emptyStateComp);
component = new EmptyStateComponent({
propsData: {
helpPagePath: 'foo',
},
}).$mount();
});
it('should render empty state SVG', () => {
expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
});
it('should render emtpy state information', () => {
expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence');
expect(
component.$el.querySelector('p').textContent,
).toContain('Continous Integration can help catch bugs by running your tests automatically');
expect(
component.$el.querySelector('p').textContent,
).toContain('Continuous Deployment can help you deliver code to your product environment');
});
it('should render a link with provided help path', () => {
expect(component.$el.querySelector('.btn-info').getAttribute('href')).toEqual('foo');
expect(component.$el.querySelector('.btn-info').textContent).toContain('Get started with Pipelines');
});
});
import Vue from 'vue';
import errorStateComp from '~/vue_pipelines_index/components/error_state';
describe('Pipelines Error State', () => {
let component;
let ErrorStateComponent;
beforeEach(() => {
ErrorStateComponent = Vue.extend(errorStateComp);
component = new ErrorStateComponent().$mount();
});
it('should render error state SVG', () => {
expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
});
it('should render emtpy state information', () => {
expect(
component.$el.querySelector('h4').textContent,
).toContain('The API failed to fetch the pipelines');
});
});
export default {
pipelines: [{
id: 115,
user: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
path: '/root/review-app/pipelines/115',
details: {
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/115',
},
duration: null,
finished_at: '2017-03-17T19:00:15.996Z',
stages: [{
name: 'build',
title: 'build: failed',
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/115#build',
},
path: '/root/review-app/pipelines/115#build',
dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=build',
},
{
name: 'review',
title: 'review: skipped',
status: {
icon: 'icon_status_skipped',
text: 'skipped',
label: 'skipped',
group: 'skipped',
has_details: true,
details_path: '/root/review-app/pipelines/115#review',
},
path: '/root/review-app/pipelines/115#review',
dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=review',
}],
artifacts: [],
manual_actions: [{
name: 'stop_review',
path: '/root/review-app/builds/3766/play',
}],
},
flags: {
latest: true,
triggered: false,
stuck: false,
yaml_errors: false,
retryable: true,
cancelable: false,
},
ref: {
name: 'thisisabranch',
path: '/root/review-app/tree/thisisabranch',
tag: false,
branch: true,
},
commit: {
id: '9e87f87625b26c42c59a2ee0398f81d20cdfe600',
short_id: '9e87f876',
title: 'Update README.md',
created_at: '2017-03-15T22:58:28.000+00:00',
parent_ids: ['3744f9226e699faec2662a8b267e5d3fd0bfff0e'],
message: 'Update README.md',
author_name: 'Root',
author_email: 'admin@example.com',
authored_date: '2017-03-15T22:58:28.000+00:00',
committer_name: 'Root',
committer_email: 'admin@example.com',
committed_date: '2017-03-15T22:58:28.000+00:00',
author: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url: 'http://localhost:3000/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
commit_path: '/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
},
retry_path: '/root/review-app/pipelines/115/retry',
created_at: '2017-03-15T22:58:33.436Z',
updated_at: '2017-03-17T19:00:15.997Z',
}],
count: {
all: 52,
running: 0,
pending: 0,
finished: 52,
},
};
import Vue from 'vue';
import navControlsComp from '~/vue_pipelines_index/components/nav_controls';
describe('Pipelines Nav Controls', () => {
let NavControlsComponent;
beforeEach(() => {
NavControlsComponent = Vue.extend(navControlsComp);
});
it('should render link to create a new pipeline', () => {
const mockData = {
newPipelinePath: 'foo',
hasCIEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
canCreatePipeline: true,
};
const component = new NavControlsComponent({
propsData: mockData,
}).$mount();
expect(component.$el.querySelector('.btn-create').textContent).toContain('Run Pipeline');
expect(component.$el.querySelector('.btn-create').getAttribute('href')).toEqual(mockData.newPipelinePath);
});
it('should not render link to create pipeline if no permission is provided', () => {
const mockData = {
newPipelinePath: 'foo',
hasCIEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
canCreatePipeline: false,
};
const component = new NavControlsComponent({
propsData: mockData,
}).$mount();
expect(component.$el.querySelector('.btn-create')).toEqual(null);
});
it('should render link for CI lint', () => {
const mockData = {
newPipelinePath: 'foo',
hasCIEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
canCreatePipeline: true,
};
const component = new NavControlsComponent({
propsData: mockData,
}).$mount();
expect(component.$el.querySelector('.btn-default').textContent).toContain('CI Lint');
expect(component.$el.querySelector('.btn-default').getAttribute('href')).toEqual(mockData.ciLintPath);
});
it('should render link to help page when CI is not enabled', () => {
const mockData = {
newPipelinePath: 'foo',
hasCIEnabled: false,
helpPagePath: 'foo',
ciLintPath: 'foo',
canCreatePipeline: true,
};
const component = new NavControlsComponent({
propsData: mockData,
}).$mount();
expect(component.$el.querySelector('.btn-info').textContent).toContain('Get started with Pipelines');
expect(component.$el.querySelector('.btn-info').getAttribute('href')).toEqual(mockData.helpPagePath);
});
it('should not render link to help page when CI is enabled', () => {
const mockData = {
newPipelinePath: 'foo',
hasCIEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
canCreatePipeline: true,
};
const component = new NavControlsComponent({
propsData: mockData,
}).$mount();
expect(component.$el.querySelector('.btn-info')).toEqual(null);
});
});
import Vue from 'vue';
import pipelinesComp from '~/vue_pipelines_index/pipelines';
import Store from '~/vue_pipelines_index/stores/pipelines_store';
import pipelinesData from './mock_data';
describe('Pipelines', () => {
preloadFixtures('static/pipelines.html.raw');
let PipelinesComponent;
beforeEach(() => {
loadFixtures('static/pipelines.html.raw');
PipelinesComponent = Vue.extend(pipelinesComp);
});
describe('successfull request', () => {
describe('with pipelines', () => {
const pipelinesInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify(pipelinesData), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(pipelinesInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesInterceptor,
);
});
it('should render table', (done) => {
const component = new PipelinesComponent({
propsData: {
store: new Store(),
},
}).$mount();
setTimeout(() => {
expect(component.$el.querySelector('.table-holder')).toBeDefined();
done();
});
});
});
describe('without pipelines', () => {
const emptyInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(emptyInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, emptyInterceptor,
);
});
it('should render empty state', (done) => {
const component = new PipelinesComponent({
propsData: {
store: new Store(),
},
}).$mount();
setTimeout(() => {
expect(component.$el.querySelector('.js-pipelines-empty-state')).toBeDefined();
done();
});
});
});
});
describe('unsuccessfull request', () => {
const errorInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 500,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(errorInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, errorInterceptor,
);
});
it('should render error state', (done) => {
const component = new PipelinesComponent({
propsData: {
store: new Store(),
},
}).$mount();
setTimeout(() => {
expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
done();
});
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment