summaryrefslogtreecommitdiff
path: root/spec/javascripts
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-03-27 12:35:10 +0200
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-03-27 12:35:10 +0200
commit1a4c60ef57d047dab6aa823f7cc50548897b74f6 (patch)
tree92bbccd1e1850837da5c7b10344ff77f0be0e7be /spec/javascripts
parent7ada193e0fd28b4a6eca1fda7dda6f0ebe6b2d72 (diff)
parent7324d6713262d7f9c563d48b82934c4a8eb72a52 (diff)
downloadgitlab-ce-1a4c60ef57d047dab6aa823f7cc50548897b74f6.tar.gz
Merge branch 'master' into feature/multi-level-container-registry-images
* master: (192 commits) Implement new service for creating user Update sentry-raven 2.0.2 -> 2.4.0 Update webmock 1.21.0 -> 1.24.6 Update spring 1.7.2 -> 2.0.1 Update simplecov 0.12.0 -> 0.14.1 Update pry-rails 0.3.4 -> 0.3.5 Update pry-byebug 3.4.1 -> 3.4.2 Update flay 2.6.1 -> 2.8.1 Remove Tags filter from Projects Explore dropdown Update capybara-screenshot 1.0.11 -> 1.0.14 Update bullet 5.2.0 -> 5.5.1 Update brakeman 3.4.1 -> 3.6.1 Remove web-console gem Update better_errors 1.0.1 -> 2.1.1 Display flash message to unauthenticated user when creating new issue Fix up emoji tests that should have failed :/ Fix RSpec/DescribeSymbol cop violations Add event limit warning all tabs Cycle Analytics Adding non_archived scope for counting projects Resolve "Gitlab administrator cannot create projects in every group" ... Conflicts: db/schema.rb
Diffstat (limited to 'spec/javascripts')
-rw-r--r--spec/javascripts/boards/board_card_spec.js3
-rw-r--r--spec/javascripts/boards/boards_store_spec.js5
-rw-r--r--spec/javascripts/boards/issue_card_spec.js3
-rw-r--r--spec/javascripts/boards/list_spec.js3
-rw-r--r--spec/javascripts/boards/modal_store_spec.js1
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js7
-rw-r--r--spec/javascripts/cycle_analytics/limit_warning_component_spec.js39
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js2
-rw-r--r--spec/javascripts/fixtures/pipelines.html.haml14
-rw-r--r--spec/javascripts/fixtures/pipelines_table.html.haml3
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js6
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js67
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js163
-rw-r--r--spec/javascripts/test_bundle.js6
-rw-r--r--spec/javascripts/user_callout_spec.js18
-rw-r--r--spec/javascripts/vue_pipelines_index/empty_state_spec.js38
-rw-r--r--spec/javascripts/vue_pipelines_index/error_state_spec.js23
-rw-r--r--spec/javascripts/vue_pipelines_index/mock_data.js107
-rw-r--r--spec/javascripts/vue_pipelines_index/nav_controls_spec.js93
-rw-r--r--spec/javascripts/vue_pipelines_index/pipelines_spec.js114
20 files changed, 688 insertions, 27 deletions
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index be31f644e20..73d18458366 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -1,10 +1,11 @@
-/* global Vue */
/* global List */
/* global ListLabel */
/* global listObj */
/* global boardsMockInterceptor */
/* global BoardService */
+import Vue from 'vue';
+
require('~/boards/models/list');
require('~/boards/models/label');
require('~/boards/stores/boards_store');
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 1d1069600fc..e21f4ca2bc0 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -1,12 +1,13 @@
/* eslint-disable comma-dangle, one-var, no-unused-vars */
-/* global Vue */
/* global BoardService */
/* global boardsMockInterceptor */
-/* global Cookies */
/* global listObj */
/* global listObjDuplicate */
/* global ListIssue */
+import Vue from 'vue';
+import Cookies from 'js-cookie';
+
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
require('~/boards/models/label');
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 4340a571017..1a5e9e9fd07 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -1,9 +1,10 @@
-/* global Vue */
/* global ListUser */
/* global ListLabel */
/* global listObj */
/* global ListIssue */
+import Vue from 'vue';
+
require('~/boards/models/issue');
require('~/boards/models/label');
require('~/boards/models/list');
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index d49d3af33d9..66fc01fa1e5 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -1,5 +1,4 @@
/* eslint-disable comma-dangle */
-/* global Vue */
/* global boardsMockInterceptor */
/* global BoardService */
/* global List */
@@ -7,6 +6,8 @@
/* global listObj */
/* global listObjDuplicate */
+import Vue from 'vue';
+
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
require('~/boards/models/label');
diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js
index 1815847f3fa..80db816aff8 100644
--- a/spec/javascripts/boards/modal_store_spec.js
+++ b/spec/javascripts/boards/modal_store_spec.js
@@ -1,4 +1,3 @@
-/* global Vue */
/* global ListIssue */
require('~/boards/models/issue');
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 75efcc06585..bc2e092db65 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -33,7 +33,8 @@ 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('.empty-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 1);
});
@@ -63,6 +64,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
setTimeout(() => {
expect(component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1);
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 0);
});
@@ -92,7 +94,8 @@ 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();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 0);
});
diff --git a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
new file mode 100644
index 00000000000..50000c5a5f5
--- /dev/null
+++ b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import limitWarningComp from '~/cycle_analytics/components/limit_warning_component';
+
+describe('Limit warning component', () => {
+ let component;
+ let LimitWarningComponent;
+
+ beforeEach(() => {
+ LimitWarningComponent = Vue.extend(limitWarningComp);
+ });
+
+ it('should not render if count is not exactly than 50', () => {
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 5,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('');
+
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 55,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('');
+ });
+
+ it('should render if count is exactly 50', () => {
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 50,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('Showing 50 events');
+ });
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 113161c21c6..848c7656a8d 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -58,7 +58,7 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper
});
describe('search', () => {
- const defaultParams = '?scope=all&utf8=✓&state=opened';
+ const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
it('should search with a single word', (done) => {
input.value = 'searchTerm';
diff --git a/spec/javascripts/fixtures/pipelines.html.haml b/spec/javascripts/fixtures/pipelines.html.haml
new file mode 100644
index 00000000000..418a38a0e2e
--- /dev/null
+++ b/spec/javascripts/fixtures/pipelines.html.haml
@@ -0,0 +1,14 @@
+%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' } }
diff --git a/spec/javascripts/fixtures/pipelines_table.html.haml b/spec/javascripts/fixtures/pipelines_table.html.haml
index fbe4a434f76..ad1682704bb 100644
--- a/spec/javascripts/fixtures/pipelines_table.html.haml
+++ b/spec/javascripts/fixtures/pipelines_table.html.haml
@@ -1,2 +1 @@
-#commit-pipeline-table-view{ data: { endpoint: "endpoint" } }
-.pipeline-svgs{ data: { "commit_icon_svg": "svg"} }
+#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } }
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
index cb068a4f879..0a830f25e29 100644
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ b/spec/javascripts/issuable_time_tracker_spec.js
@@ -1,7 +1,7 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, space-before-function-paren, func-call-spacing, no-spaced-func, semi, max-len, quotes, space-infix-ops, padded-blocks */
+
+import Vue from 'vue';
-require('jquery');
-require('vue');
require('~/issuable/time_tracking/components/time_tracker');
function initTimeTrackingComponent(opts) {
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index f4d3e77e515..d2e24eb7eb2 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -163,5 +163,72 @@ require('~/lib/utils/common_utils');
expect(gl.utils.isMetaClick(e)).toBe(true);
});
});
+
+ describe('gl.utils.backOff', () => {
+ it('solves the promise from the callback', (done) => {
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff((next, stop) => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then((resp) => {
+ stop(resp);
+ })
+ )).then((respBackoff) => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
+ });
+ });
+
+ it('catches the rejected promise from the callback ', (done) => {
+ const errorMessage = 'Mistakes were made!';
+ gl.utils.backOff((next, stop) => {
+ new Promise((resolve, reject) => {
+ reject(new Error(errorMessage));
+ }).then((resp) => {
+ stop(resp);
+ }).catch(err => stop(err));
+ }).catch((errBackoffResp) => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe(errorMessage);
+ done();
+ });
+ });
+
+ it('solves the promise correctly after retrying a third time', (done) => {
+ let numberOfCalls = 1;
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff((next, stop) => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then((resp) => {
+ if (numberOfCalls < 3) {
+ numberOfCalls += 1;
+ next();
+ } else {
+ stop(resp);
+ }
+ })
+ )).then((respBackoff) => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ expect(numberOfCalls).toBe(3);
+ done();
+ });
+ }, 10000);
+
+ it('rejects the backOff promise after timing out', (done) => {
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff(next => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then(() => {
+ setTimeout(next(), 5000); // it will time out
+ })
+ ), 3000).catch((errBackoffResp) => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
+ done();
+ });
+ }, 10000);
+ });
});
})();
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
new file mode 100644
index 00000000000..c794a632417
--- /dev/null
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -0,0 +1,163 @@
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+import Poll from '~/lib/utils/poll';
+
+Vue.use(VueResource);
+
+class ServiceMock {
+ constructor(endpoint) {
+ this.service = Vue.resource(endpoint);
+ }
+
+ fetch() {
+ return this.service.get();
+ }
+}
+
+describe('Poll', () => {
+ let callbacks;
+
+ beforeEach(() => {
+ callbacks = {
+ success: () => {},
+ error: () => {},
+ };
+
+ spyOn(callbacks, 'success');
+ spyOn(callbacks, 'error');
+ });
+
+ it('calls the success callback when no header for interval is provided', (done) => {
+ const successInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200 }));
+ };
+
+ Vue.http.interceptors.push(successInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, successInterceptor);
+ });
+
+ it('calls the error callback whe the http request returns an error', (done) => {
+ const errorInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 500 }));
+ };
+
+ Vue.http.interceptors.push(errorInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).not.toHaveBeenCalled();
+ expect(callbacks.error).toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, errorInterceptor);
+ });
+
+ it('should call the success callback when the interval header is -1', (done) => {
+ const intervalInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': -1 } }));
+ };
+
+ Vue.http.interceptors.push(intervalInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, intervalInterceptor);
+ });
+
+ it('starts polling when http status is 200 and interval header is provided', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(2);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 5);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+
+ describe('stop', () => {
+ it('stops polling when method is called', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ const Polling = new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: () => {
+ Polling.stop();
+ },
+ errorCallback: callbacks.error,
+ });
+
+ spyOn(Polling, 'stop').and.callThrough();
+
+ Polling.makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(1);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(Polling.stop).toHaveBeenCalled();
+ done();
+ }, 100);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+ });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 5cdb6473eda..d658f680f97 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -8,9 +8,6 @@ jasmine.getJSONFixtures().fixturesPath = 'base/spec/javascripts/fixtures';
require('~/commons/index.js');
window.$ = window.jQuery = require('jquery');
window._ = require('underscore');
-window.Cookies = require('js-cookie');
-window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
// stub expected globals
window.gl = window.gl || {};
@@ -38,7 +35,8 @@ testsContext.keys().forEach(function (path) {
if (process.env.BABEL_ENV === 'coverage') {
// exempt these files from the coverage report
const troubleMakers = [
- './blob_edit/blob_edit_bundle.js',
+ './blob_edit/blob_bundle.js',
+ './boards/boards_bundle.js',
'./cycle_analytics/components/stage_plan_component.js',
'./cycle_analytics/components/stage_staging_component.js',
'./cycle_analytics/components/stage_test_component.js',
diff --git a/spec/javascripts/user_callout_spec.js b/spec/javascripts/user_callout_spec.js
index 205e72af600..2398149d3ad 100644
--- a/spec/javascripts/user_callout_spec.js
+++ b/spec/javascripts/user_callout_spec.js
@@ -1,7 +1,7 @@
-const UserCallout = require('~/user_callout');
+import Cookies from 'js-cookie';
+import UserCallout from '~/user_callout';
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
-const Cookie = window.Cookies;
describe('UserCallout', function () {
const fixtureName = 'static/user_callout.html.raw';
@@ -9,7 +9,7 @@ describe('UserCallout', function () {
beforeEach(() => {
loadFixtures(fixtureName);
- Cookie.remove(USER_CALLOUT_COOKIE);
+ Cookies.remove(USER_CALLOUT_COOKIE);
this.userCallout = new UserCallout();
this.closeButton = $('.close-user-callout');
@@ -18,25 +18,25 @@ describe('UserCallout', function () {
});
it('does not show when cookie is set not defined', () => {
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeUndefined();
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeUndefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
it('shows when cookie is set to false', () => {
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
+ Cookies.set(USER_CALLOUT_COOKIE, 'false');
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined();
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeDefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
it('hides when user clicks on the dismiss-icon', () => {
this.closeButton.click();
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true');
});
it('hides when user clicks on the "check it out" button', () => {
this.userCalloutBtn.click();
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true');
});
});
@@ -46,7 +46,7 @@ describe('UserCallout when cookie is present', function () {
beforeEach(() => {
loadFixtures(fixtureName);
- Cookie.set(USER_CALLOUT_COOKIE, 'true');
+ Cookies.set(USER_CALLOUT_COOKIE, 'true');
this.userCallout = new UserCallout();
this.userCalloutContainer = $('.user-callout');
});
diff --git a/spec/javascripts/vue_pipelines_index/empty_state_spec.js b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
new file mode 100644
index 00000000000..733337168dc
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
@@ -0,0 +1,38 @@
+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');
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/error_state_spec.js b/spec/javascripts/vue_pipelines_index/error_state_spec.js
new file mode 100644
index 00000000000..524e018b1fa
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/error_state_spec.js
@@ -0,0 +1,23 @@
+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');
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/mock_data.js b/spec/javascripts/vue_pipelines_index/mock_data.js
new file mode 100644
index 00000000000..2365a662b9f
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/mock_data.js
@@ -0,0 +1,107 @@
+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,
+ },
+};
diff --git a/spec/javascripts/vue_pipelines_index/nav_controls_spec.js b/spec/javascripts/vue_pipelines_index/nav_controls_spec.js
new file mode 100644
index 00000000000..659c4854a56
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/nav_controls_spec.js
@@ -0,0 +1,93 @@
+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);
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/pipelines_spec.js b/spec/javascripts/vue_pipelines_index/pipelines_spec.js
new file mode 100644
index 00000000000..725f6cb2d7a
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/pipelines_spec.js
@@ -0,0 +1,114 @@
+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();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ 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('.empty-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ 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();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+});