summaryrefslogtreecommitdiff
path: root/spec/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'spec/javascripts')
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js45
-rw-r--r--spec/javascripts/boards/components/board_spec.js112
-rw-r--r--spec/javascripts/build_spec.js2
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js4
-rw-r--r--spec/javascripts/commits_spec.js26
-rw-r--r--spec/javascripts/datetime_utility_spec.js20
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js18
-rw-r--r--spec/javascripts/environments/environment_spec.js2
-rw-r--r--spec/javascripts/environments/environment_table_spec.js2
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js79
-rw-r--r--spec/javascripts/fixtures/boards.rb28
-rw-r--r--spec/javascripts/fixtures/issuable_filter.html.haml2
-rw-r--r--spec/javascripts/gl_emoji_spec.js31
-rw-r--r--spec/javascripts/groups/group_item_spec.js102
-rw-r--r--spec/javascripts/groups/groups_spec.js64
-rw-r--r--spec/javascripts/groups/mock_data.js110
-rw-r--r--spec/javascripts/issuable_spec.js16
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js8
-rw-r--r--spec/javascripts/notes_spec.js39
-rw-r--r--spec/javascripts/test_bundle.js1
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js4
21 files changed, 641 insertions, 74 deletions
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index 45d12e252c4..832877de71c 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -19,6 +19,7 @@ describe('Issue boards new issue form', () => {
};
},
};
+
const submitIssue = () => {
vm.$el.querySelector('.btn-success').click();
};
@@ -107,7 +108,7 @@ describe('Issue boards new issue form', () => {
setTimeout(() => {
submitIssue();
- expect(vm.$el.querySelector('.btn-success').disbled).not.toBe(true);
+ expect(vm.$el.querySelector('.btn-success').disabled).toBe(false);
done();
}, 0);
});
@@ -115,36 +116,43 @@ describe('Issue boards new issue form', () => {
it('clears title after submit', (done) => {
vm.title = 'submit issue';
- setTimeout(() => {
+ Vue.nextTick(() => {
submitIssue();
- expect(vm.title).toBe('');
- done();
- }, 0);
+ setTimeout(() => {
+ expect(vm.title).toBe('');
+ done();
+ }, 0);
+ });
});
- it('adds new issue to list after submit', (done) => {
+ it('adds new issue to top of list after submit request', (done) => {
vm.title = 'submit issue';
setTimeout(() => {
submitIssue();
- expect(list.issues.length).toBe(2);
- expect(list.issues[1].title).toBe('submit issue');
- expect(list.issues[1].subscribed).toBe(true);
- done();
+ setTimeout(() => {
+ expect(list.issues.length).toBe(2);
+ expect(list.issues[0].title).toBe('submit issue');
+ expect(list.issues[0].subscribed).toBe(true);
+ done();
+ }, 0);
}, 0);
});
it('sets detail issue after submit', (done) => {
+ expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe(undefined);
vm.title = 'submit issue';
setTimeout(() => {
submitIssue();
- expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
- done();
- });
+ setTimeout(() => {
+ expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
+ done();
+ }, 0);
+ }, 0);
});
it('sets detail list after submit', (done) => {
@@ -153,8 +161,10 @@ describe('Issue boards new issue form', () => {
setTimeout(() => {
submitIssue();
- expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
- done();
+ setTimeout(() => {
+ expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
+ done();
+ }, 0);
}, 0);
});
});
@@ -169,13 +179,12 @@ describe('Issue boards new issue form', () => {
setTimeout(() => {
expect(list.issues.length).toBe(1);
done();
- }, 500);
+ }, 0);
}, 0);
});
it('shows error', (done) => {
vm.title = 'error';
- submitIssue();
setTimeout(() => {
submitIssue();
@@ -183,7 +192,7 @@ describe('Issue boards new issue form', () => {
setTimeout(() => {
expect(vm.error).toBe(true);
done();
- }, 500);
+ }, 0);
}, 0);
});
});
diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js
new file mode 100644
index 00000000000..c4e8966ad6c
--- /dev/null
+++ b/spec/javascripts/boards/components/board_spec.js
@@ -0,0 +1,112 @@
+import Vue from 'vue';
+import '~/boards/services/board_service';
+import '~/boards/components/board';
+import '~/boards/models/list';
+
+describe('Board component', () => {
+ let vm;
+ let el;
+
+ beforeEach((done) => {
+ loadFixtures('boards/show.html.raw');
+
+ el = document.createElement('div');
+ document.body.appendChild(el);
+
+ // eslint-disable-next-line no-undef
+ gl.boardService = new BoardService('/', '/', 1);
+
+ vm = new gl.issueBoards.Board({
+ propsData: {
+ boardId: '1',
+ disabled: false,
+ issueLinkBase: '/',
+ rootPath: '/',
+ // eslint-disable-next-line no-undef
+ list: new List({
+ id: 1,
+ position: 0,
+ title: 'test',
+ list_type: 'backlog',
+ }),
+ },
+ }).$mount(el);
+
+ Vue.nextTick(done);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ // remove the component from the DOM
+ document.querySelector('.board').remove();
+
+ localStorage.removeItem(`boards.${vm.boardId}.${vm.list.type}.expanded`);
+ });
+
+ it('board is expandable when list type is backlog', () => {
+ expect(
+ vm.$el.classList.contains('is-expandable'),
+ ).toBe(true);
+ });
+
+ it('board is expandable when list type is closed', (done) => {
+ vm.list.type = 'closed';
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.classList.contains('is-expandable'),
+ ).toBe(true);
+
+ done();
+ });
+ });
+
+ it('board is not expandable when list type is label', (done) => {
+ vm.list.type = 'label';
+ vm.list.isExpandable = false;
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.classList.contains('is-expandable'),
+ ).toBe(false);
+
+ done();
+ });
+ });
+
+ it('collapses when clicking header', (done) => {
+ vm.$el.querySelector('.board-header').click();
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.classList.contains('is-collapsed'),
+ ).toBe(true);
+
+ done();
+ });
+ });
+
+ it('created sets isExpanded to true from localStorage', (done) => {
+ vm.$el.querySelector('.board-header').click();
+
+ return Vue.nextTick()
+ .then(() => {
+ expect(
+ vm.$el.classList.contains('is-collapsed'),
+ ).toBe(true);
+
+ // call created manually
+ vm.$options.created[0].call(vm);
+
+ return Vue.nextTick();
+ })
+ .then(() => {
+ expect(
+ vm.$el.classList.contains('is-collapsed'),
+ ).toBe(true);
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/build_spec.js b/spec/javascripts/build_spec.js
index 461908f3fde..4c8a48580d7 100644
--- a/spec/javascripts/build_spec.js
+++ b/spec/javascripts/build_spec.js
@@ -58,7 +58,7 @@ describe('Build', () => {
it('displays the remove date correctly', () => {
const removeDateElement = document.querySelector('.js-artifacts-remove');
- expect(removeDateElement.innerText.trim()).toBe('1 year');
+ expect(removeDateElement.innerText.trim()).toBe('1 year remaining');
});
});
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 398c593eec2..ebfd60198b2 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -71,7 +71,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
it('should render a table with the received pipelines', (done) => {
setTimeout(() => {
- expect(this.component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1);
+ expect(this.component.$el.querySelectorAll('.ci-table .commit').length).toEqual(1);
expect(this.component.$el.querySelector('.realtime-loading')).toBe(null);
expect(this.component.$el.querySelector('.empty-state')).toBe(null);
expect(this.component.$el.querySelector('.js-pipelines-error-state')).toBe(null);
@@ -108,7 +108,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
expect(this.component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
expect(this.component.$el.querySelector('.realtime-loading')).toBe(null);
expect(this.component.$el.querySelector('.js-empty-state')).toBe(null);
- expect(this.component.$el.querySelector('table')).toBe(null);
+ expect(this.component.$el.querySelector('.ci-table')).toBe(null);
done();
}, 0);
});
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index 187db7485a5..44a4386b250 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -28,6 +28,32 @@ import '~/commits';
expect(CommitsList).toBeDefined();
});
+ describe('processCommits', () => {
+ it('should join commit headers', () => {
+ CommitsList.$contentList = $(`
+ <div>
+ <li class="commit-header" data-day="2016-09-20">
+ <span class="day">20 Sep, 2016</span>
+ <span class="commits-count">1 commit</span>
+ </li>
+ <li class="commit"></li>
+ </div>
+ `);
+
+ const data = `
+ <li class="commit-header" data-day="2016-09-20">
+ <span class="day">20 Sep, 2016</span>
+ <span class="commits-count">1 commit</span>
+ </li>
+ <li class="commit"></li>
+ `;
+
+ // The last commit header should be removed
+ // since the previous one has the same data-day value.
+ expect(CommitsList.processCommits(data).find('li.commit-header').length).toBe(0);
+ });
+ });
+
describe('on entering input', () => {
let ajaxSpy;
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index e347c980c78..c82ad0bea48 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -2,6 +2,26 @@ import '~/lib/utils/datetime_utility';
(() => {
describe('Date time utils', () => {
+ describe('timeFor', () => {
+ it('returns `past due` when in past', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() - 1);
+
+ expect(
+ gl.utils.timeFor(date),
+ ).toBe('Past due');
+ });
+
+ it('returns remaining time when in the future', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() + 1);
+
+ expect(
+ gl.utils.timeFor(date),
+ ).toBe('1 year remaining');
+ });
+ });
+
describe('get day name', () => {
it('should return Sunday', () => {
const day = gl.utils.getDayName(new Date('07/17/2016'));
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index 793ab8c451d..a4b98f6140d 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -39,9 +39,15 @@ describe('Deploy keys key', () => {
).toBe(`created ${gl.utils.getTimeago().format(deployKey.created_at)}`);
});
+ it('shows edit button', () => {
+ expect(
+ vm.$el.querySelectorAll('.btn')[0].textContent.trim(),
+ ).toBe('Edit');
+ });
+
it('shows remove button', () => {
expect(
- vm.$el.querySelector('.btn').textContent.trim(),
+ vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
).toBe('Remove');
});
@@ -71,9 +77,15 @@ describe('Deploy keys key', () => {
setTimeout(done);
});
+ it('shows edit button', () => {
+ expect(
+ vm.$el.querySelectorAll('.btn')[0].textContent.trim(),
+ ).toBe('Edit');
+ });
+
it('shows enable button', () => {
expect(
- vm.$el.querySelector('.btn').textContent.trim(),
+ vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
).toBe('Enable');
});
@@ -82,7 +94,7 @@ describe('Deploy keys key', () => {
Vue.nextTick(() => {
expect(
- vm.$el.querySelector('.btn').textContent.trim(),
+ vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
).toBe('Disable');
done();
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index c31642ac788..6639a6b5e7b 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -271,7 +271,7 @@ describe('Environment', () => {
// wait for next async request
setTimeout(() => {
expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1);
- expect(component.$el.querySelector('td.text-center > a.btn').textContent).toContain('Show all');
+ expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain('Show all');
Vue.http.interceptors = _.without(Vue.http.interceptors, folderInterceptor);
done();
diff --git a/spec/javascripts/environments/environment_table_spec.js b/spec/javascripts/environments/environment_table_spec.js
index effbc6c3ee1..2862971bec4 100644
--- a/spec/javascripts/environments/environment_table_spec.js
+++ b/spec/javascripts/environments/environment_table_spec.js
@@ -29,6 +29,6 @@ describe('Environment item', () => {
},
}).$mount();
- expect(component.$el.tagName).toEqual('TABLE');
+ expect(component.$el.getAttribute('class')).toContain('ci-table');
});
});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 6e59ee96c6b..6d00d71f145 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -97,6 +97,49 @@ describe('Filtered Search Manager', () => {
});
});
+ describe('searchState', () => {
+ beforeEach(() => {
+ spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
+ });
+
+ it('should blur button', () => {
+ const e = {
+ currentTarget: {
+ blur: () => {},
+ },
+ };
+ spyOn(e.currentTarget, 'blur').and.callThrough();
+ manager.searchState(e);
+
+ expect(e.currentTarget.blur).toHaveBeenCalled();
+ });
+
+ it('should not call search if there is no state', () => {
+ const e = {
+ currentTarget: {
+ blur: () => {},
+ },
+ };
+
+ manager.searchState(e);
+ expect(gl.FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
+ });
+
+ it('should call search when there is state', () => {
+ const e = {
+ currentTarget: {
+ blur: () => {},
+ dataset: {
+ state: 'opened',
+ },
+ },
+ };
+
+ manager.searchState(e);
+ expect(gl.FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
+ });
+ });
+
describe('search', () => {
const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
@@ -316,42 +359,6 @@ describe('Filtered Search Manager', () => {
});
});
- describe('unselects token', () => {
- beforeEach(() => {
- tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
- ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug', true)}
- ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')}
- ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~awesome')}
- `);
- });
-
- it('unselects token when input is clicked', () => {
- const selectedToken = tokensContainer.querySelector('.js-visual-token .selected');
-
- expect(selectedToken.classList.contains('selected')).toEqual(true);
- expect(gl.FilteredSearchVisualTokens.unselectTokens).not.toHaveBeenCalled();
-
- // Click directly on input attached to document
- // so that the click event will propagate properly
- document.querySelector('.filtered-search').click();
-
- expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
- expect(selectedToken.classList.contains('selected')).toEqual(false);
- });
-
- it('unselects token when document.body is clicked', () => {
- const selectedToken = tokensContainer.querySelector('.js-visual-token .selected');
-
- expect(selectedToken.classList.contains('selected')).toEqual(true);
- expect(gl.FilteredSearchVisualTokens.unselectTokens).not.toHaveBeenCalled();
-
- document.body.click();
-
- expect(selectedToken.classList.contains('selected')).toEqual(false);
- expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
- });
- });
-
describe('toggleInputContainerFocus', () => {
it('toggles on focus', () => {
input.focus();
diff --git a/spec/javascripts/fixtures/boards.rb b/spec/javascripts/fixtures/boards.rb
new file mode 100644
index 00000000000..d7c3dc0a235
--- /dev/null
+++ b/spec/javascripts/fixtures/boards.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Projects::BoardsController, '(JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ let(:admin) { create(:admin) }
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:project) { create(:project, :repository, namespace: namespace, path: 'boards-project') }
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('boards/')
+ end
+
+ before(:each) do
+ sign_in(admin)
+ end
+
+ it 'boards/show.html.raw' do |example|
+ get(:index,
+ namespace_id: project.namespace,
+ project_id: project)
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/fixtures/issuable_filter.html.haml b/spec/javascripts/fixtures/issuable_filter.html.haml
index ae745b292e6..84fa5395cb8 100644
--- a/spec/javascripts/fixtures/issuable_filter.html.haml
+++ b/spec/javascripts/fixtures/issuable_filter.html.haml
@@ -1,6 +1,6 @@
%form.js-filter-form{action: '/user/project/issues?scope=all&state=closed'}
%input{id: 'utf8', name: 'utf8', value: '✓'}
- %input{id: 'check_all_issues', name: 'check_all_issues'}
+ %input{id: 'check-all-issues', name: 'check-all-issues'}
%input{id: 'search', name: 'search'}
%input{id: 'author_id', name: 'author_id'}
%input{id: 'assignee_id', name: 'assignee_id'}
diff --git a/spec/javascripts/gl_emoji_spec.js b/spec/javascripts/gl_emoji_spec.js
index b2b46640e5b..a09e0072fa8 100644
--- a/spec/javascripts/gl_emoji_spec.js
+++ b/spec/javascripts/gl_emoji_spec.js
@@ -192,6 +192,9 @@ describe('gl_emoji', () => {
});
describe('isFlagEmoji', () => {
+ it('should gracefully handle empty string', () => {
+ expect(isFlagEmoji('')).toBeFalsy();
+ });
it('should detect flag_ac', () => {
expect(isFlagEmoji('🇦🇨')).toBeTruthy();
});
@@ -216,6 +219,9 @@ describe('gl_emoji', () => {
});
describe('isKeycapEmoji', () => {
+ it('should gracefully handle empty string', () => {
+ expect(isKeycapEmoji('')).toBeFalsy();
+ });
it('should detect one(keycap)', () => {
expect(isKeycapEmoji('1️⃣')).toBeTruthy();
});
@@ -231,6 +237,9 @@ describe('gl_emoji', () => {
});
describe('isSkinToneComboEmoji', () => {
+ it('should gracefully handle empty string', () => {
+ expect(isSkinToneComboEmoji('')).toBeFalsy();
+ });
it('should detect hand_splayed_tone5', () => {
expect(isSkinToneComboEmoji('🖐🏿')).toBeTruthy();
});
@@ -255,6 +264,9 @@ describe('gl_emoji', () => {
});
describe('isHorceRacingSkinToneComboEmoji', () => {
+ it('should gracefully handle empty string', () => {
+ expect(isHorceRacingSkinToneComboEmoji('')).toBeFalsy();
+ });
it('should detect horse_racing_tone2', () => {
expect(isHorceRacingSkinToneComboEmoji('🏇🏼')).toBeTruthy();
});
@@ -264,6 +276,9 @@ describe('gl_emoji', () => {
});
describe('isPersonZwjEmoji', () => {
+ it('should gracefully handle empty string', () => {
+ expect(isPersonZwjEmoji('')).toBeFalsy();
+ });
it('should detect couple_mm', () => {
expect(isPersonZwjEmoji('👨‍❤️‍👨')).toBeTruthy();
});
@@ -300,6 +315,22 @@ describe('gl_emoji', () => {
});
describe('isEmojiUnicodeSupported', () => {
+ it('should gracefully handle empty string with unicode support', () => {
+ const isSupported = isEmojiUnicodeSupported(
+ { '1.0': true },
+ '',
+ '1.0',
+ );
+ expect(isSupported).toBeTruthy();
+ });
+ it('should gracefully handle empty string without unicode support', () => {
+ const isSupported = isEmojiUnicodeSupported(
+ {},
+ '',
+ '1.0',
+ );
+ expect(isSupported).toBeFalsy();
+ });
it('bomb(6.0) with 6.0 support', () => {
const emojiKey = 'bomb';
const unicodeSupportMap = Object.assign({}, emptySupportMap, {
diff --git a/spec/javascripts/groups/group_item_spec.js b/spec/javascripts/groups/group_item_spec.js
new file mode 100644
index 00000000000..25e10552d95
--- /dev/null
+++ b/spec/javascripts/groups/group_item_spec.js
@@ -0,0 +1,102 @@
+import Vue from 'vue';
+import groupItemComponent from '~/groups/components/group_item.vue';
+import GroupsStore from '~/groups/stores/groups_store';
+import { group1 } from './mock_data';
+
+describe('Groups Component', () => {
+ let GroupItemComponent;
+ let component;
+ let store;
+ let group;
+
+ describe('group with default data', () => {
+ beforeEach((done) => {
+ GroupItemComponent = Vue.extend(groupItemComponent);
+ store = new GroupsStore();
+ group = store.decorateGroup(group1);
+
+ component = new GroupItemComponent({
+ propsData: {
+ group,
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ done();
+ });
+ });
+
+ afterEach(() => {
+ component.$destroy();
+ });
+
+ it('should render the group item correctly', () => {
+ expect(component.$el.classList.contains('group-row')).toBe(true);
+ expect(component.$el.classList.contains('.no-description')).toBe(false);
+ expect(component.$el.querySelector('.number-projects').textContent).toContain(group.numberProjects);
+ expect(component.$el.querySelector('.number-users').textContent).toContain(group.numberUsers);
+ expect(component.$el.querySelector('.group-visibility')).toBeDefined();
+ expect(component.$el.querySelector('.avatar-container')).toBeDefined();
+ expect(component.$el.querySelector('.title').textContent).toContain(group.name);
+ expect(component.$el.querySelector('.access-type').textContent).toContain(group.permissions.humanGroupAccess);
+ expect(component.$el.querySelector('.description').textContent).toContain(group.description);
+ expect(component.$el.querySelector('.edit-group')).toBeDefined();
+ expect(component.$el.querySelector('.leave-group')).toBeDefined();
+ });
+ });
+
+ describe('group without description', () => {
+ beforeEach((done) => {
+ GroupItemComponent = Vue.extend(groupItemComponent);
+ store = new GroupsStore();
+ group1.description = '';
+ group = store.decorateGroup(group1);
+
+ component = new GroupItemComponent({
+ propsData: {
+ group,
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ done();
+ });
+ });
+
+ afterEach(() => {
+ component.$destroy();
+ });
+
+ it('should render group item correctly', () => {
+ expect(component.$el.querySelector('.description').textContent).toBe('');
+ expect(component.$el.classList.contains('.no-description')).toBe(false);
+ });
+ });
+
+ describe('user has not access to group', () => {
+ beforeEach((done) => {
+ GroupItemComponent = Vue.extend(groupItemComponent);
+ store = new GroupsStore();
+ group1.permissions.human_group_access = null;
+ group = store.decorateGroup(group1);
+
+ component = new GroupItemComponent({
+ propsData: {
+ group,
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ done();
+ });
+ });
+
+ afterEach(() => {
+ component.$destroy();
+ });
+
+ it('should not display access type', () => {
+ expect(component.$el.querySelector('.access-type')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/groups/groups_spec.js b/spec/javascripts/groups/groups_spec.js
new file mode 100644
index 00000000000..2a77f7259da
--- /dev/null
+++ b/spec/javascripts/groups/groups_spec.js
@@ -0,0 +1,64 @@
+import Vue from 'vue';
+import groupFolderComponent from '~/groups/components/group_folder.vue';
+import groupItemComponent from '~/groups/components/group_item.vue';
+import groupsComponent from '~/groups/components/groups.vue';
+import GroupsStore from '~/groups/stores/groups_store';
+import { groupsData } from './mock_data';
+
+describe('Groups Component', () => {
+ let GroupsComponent;
+ let store;
+ let component;
+ let groups;
+
+ beforeEach((done) => {
+ Vue.component('group-folder', groupFolderComponent);
+ Vue.component('group-item', groupItemComponent);
+
+ store = new GroupsStore();
+ groups = store.setGroups(groupsData.groups);
+
+ store.storePagination(groupsData.pagination);
+
+ GroupsComponent = Vue.extend(groupsComponent);
+
+ component = new GroupsComponent({
+ propsData: {
+ groups: store.state.groups,
+ pageInfo: store.state.pageInfo,
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ done();
+ });
+ });
+
+ afterEach(() => {
+ component.$destroy();
+ });
+
+ describe('with data', () => {
+ it('should render a list of groups', () => {
+ expect(component.$el.classList.contains('groups-list-tree-container')).toBe(true);
+ expect(component.$el.querySelector('#group-12')).toBeDefined();
+ expect(component.$el.querySelector('#group-1119')).toBeDefined();
+ expect(component.$el.querySelector('#group-1120')).toBeDefined();
+ });
+
+ it('should render group and its subgroup', () => {
+ const lists = component.$el.querySelectorAll('.group-list-tree');
+
+ expect(lists.length).toBe(3); // one parent and two subgroups
+
+ expect(lists[0].querySelector('#group-1119').classList.contains('is-open')).toBe(true);
+ expect(lists[0].querySelector('#group-1119').classList.contains('has-subgroups')).toBe(true);
+
+ expect(lists[2].querySelector('#group-1120').textContent).toContain(groups[1119].subGroups[1120].name);
+ });
+
+ it('should remove prefix of parent group', () => {
+ expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4');
+ });
+ });
+});
diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js
new file mode 100644
index 00000000000..1c0ec7a97d0
--- /dev/null
+++ b/spec/javascripts/groups/mock_data.js
@@ -0,0 +1,110 @@
+const group1 = {
+ id: '12',
+ name: 'level1',
+ path: 'level1',
+ description: 'foo',
+ visibility: 'public',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/groups/level1',
+ full_name: 'level1',
+ full_path: 'level1',
+ parent_id: null,
+ created_at: '2017-05-15T19:01:23.670Z',
+ updated_at: '2017-05-15T19:01:23.670Z',
+ number_projects_with_delimiter: '1',
+ number_users_with_delimiter: '1',
+ has_subgroups: true,
+ permissions: {
+ human_group_access: 'Master',
+ },
+};
+
+// This group has no direct parent, should be placed as subgroup of group1
+const group14 = {
+ id: 1128,
+ name: 'level4',
+ path: 'level4',
+ description: 'foo',
+ visibility: 'public',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/groups/level1/level2/level3/level4',
+ full_name: 'level1 / level2 / level3 / level4',
+ full_path: 'level1/level2/level3/level4',
+ parent_id: 1127,
+ created_at: '2017-05-15T19:02:01.645Z',
+ updated_at: '2017-05-15T19:02:01.645Z',
+ number_projects_with_delimiter: '1',
+ number_users_with_delimiter: '1',
+ has_subgroups: true,
+ permissions: {
+ human_group_access: 'Master',
+ },
+};
+
+const group2 = {
+ id: 1119,
+ name: 'devops',
+ path: 'devops',
+ description: 'foo',
+ visibility: 'public',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/groups/devops',
+ full_name: 'devops',
+ full_path: 'devops',
+ parent_id: null,
+ created_at: '2017-05-11T19:35:09.635Z',
+ updated_at: '2017-05-11T19:35:09.635Z',
+ number_projects_with_delimiter: '1',
+ number_users_with_delimiter: '1',
+ has_subgroups: true,
+ permissions: {
+ human_group_access: 'Master',
+ },
+};
+
+const group21 = {
+ id: 1120,
+ name: 'chef',
+ path: 'chef',
+ description: 'foo',
+ visibility: 'public',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/groups/devops/chef',
+ full_name: 'devops / chef',
+ full_path: 'devops/chef',
+ parent_id: 1119,
+ created_at: '2017-05-11T19:51:04.060Z',
+ updated_at: '2017-05-11T19:51:04.060Z',
+ number_projects_with_delimiter: '1',
+ number_users_with_delimiter: '1',
+ has_subgroups: true,
+ permissions: {
+ human_group_access: 'Master',
+ },
+};
+
+const groupsData = {
+ groups: [group1, group14, group2, group21],
+ pagination: {
+ Date: 'Mon, 22 May 2017 22:31:52 GMT',
+ 'X-Prev-Page': '1',
+ 'X-Content-Type-Options': 'nosniff',
+ 'X-Total': '31',
+ 'Transfer-Encoding': 'chunked',
+ 'X-Runtime': '0.611144',
+ 'X-Xss-Protection': '1; mode=block',
+ 'X-Request-Id': 'f5db8368-3ce5-4aa4-89d2-a125d9dead09',
+ 'X-Ua-Compatible': 'IE=edge',
+ 'X-Per-Page': '20',
+ Link: '<http://localhost:3000/dashboard/groups.json?page=1&per_page=20>; rel="prev", <http://localhost:3000/dashboard/groups.json?page=1&per_page=20>; rel="first", <http://localhost:3000/dashboard/groups.json?page=2&per_page=20>; rel="last"',
+ 'X-Next-Page': '',
+ Etag: 'W/"a82f846947136271cdb7d55d19ef33d2"',
+ 'X-Frame-Options': 'DENY',
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Cache-Control': 'max-age=0, private, must-revalidate',
+ 'X-Total-Pages': '2',
+ 'X-Page': '2',
+ },
+};
+
+export { groupsData, group1 };
diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js
index 49fa2cb8367..45f55395d3a 100644
--- a/spec/javascripts/issuable_spec.js
+++ b/spec/javascripts/issuable_spec.js
@@ -1,7 +1,7 @@
-/* global Issuable */
+/* global IssuableIndex */
import '~/lib/utils/url_utility';
-import '~/issuable';
+import '~/issuable_index';
(() => {
const BASE_URL = '/user/project/issues?scope=all&state=closed';
@@ -24,11 +24,11 @@ import '~/issuable';
beforeEach(() => {
loadFixtures('static/issuable_filter.html.raw');
- Issuable.init();
+ IssuableIndex.init();
});
it('should be defined', () => {
- expect(window.Issuable).toBeDefined();
+ expect(window.IssuableIndex).toBeDefined();
});
describe('filtering', () => {
@@ -43,7 +43,7 @@ import '~/issuable';
it('should contain only the default parameters', () => {
spyOn(gl.utils, 'visitUrl');
- Issuable.filterResults($filtersForm);
+ IssuableIndex.filterResults($filtersForm);
expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + DEFAULT_PARAMS);
});
@@ -52,7 +52,7 @@ import '~/issuable';
spyOn(gl.utils, 'visitUrl');
updateForm({ search: 'broken' }, $filtersForm);
- Issuable.filterResults($filtersForm);
+ IssuableIndex.filterResults($filtersForm);
const params = `${DEFAULT_PARAMS}&search=broken`;
expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
@@ -64,14 +64,14 @@ import '~/issuable';
// initial filter
updateForm({ milestone_title: 'v1.0' }, $filtersForm);
- Issuable.filterResults($filtersForm);
+ IssuableIndex.filterResults($filtersForm);
let params = `${DEFAULT_PARAMS}&milestone_title=v1.0`;
expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
// update filter
updateForm({ label_name: 'Frontend' }, $filtersForm);
- Issuable.filterResults($filtersForm);
+ IssuableIndex.filterResults($filtersForm);
params = `${DEFAULT_PARAMS}&milestone_title=v1.0&label_name=Frontend`;
expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
});
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index e3938a77680..52cf217c25f 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -150,6 +150,14 @@ import '~/lib/utils/common_utils';
const value = gl.utils.getParameterByName('fakeParameter');
expect(value).toBe(null);
});
+
+ it('should return valid paramentes if URL is provided', () => {
+ let value = gl.utils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar');
+ expect(value).toBe('bar');
+
+ value = gl.utils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu');
+ expect(value).toBe('canchu');
+ });
});
describe('gl.utils.normalizedHeaders', () => {
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 24335614e09..bfd8b8648a6 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -461,6 +461,45 @@ import '~/notes';
});
});
+ describe('update comment with script tags', () => {
+ const sampleComment = '<script></script>';
+ const updatedComment = '<script></script>';
+ const note = {
+ id: 1234,
+ html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
+ <div class="note-text">${sampleComment}</div>
+ </li>`,
+ note: sampleComment,
+ valid: true
+ };
+ let $form;
+ let $notesContainer;
+
+ beforeEach(() => {
+ this.notes = new Notes('', []);
+ window.gon.current_username = 'root';
+ window.gon.current_user_fullname = 'Administrator';
+ $form = $('form.js-main-target-form');
+ $notesContainer = $('ul.main-notes-list');
+ $form.find('textarea.js-note-text').html(sampleComment);
+ });
+
+ it('should not render a script tag', () => {
+ const deferred = $.Deferred();
+ spyOn($, 'ajax').and.returnValue(deferred.promise());
+ $('.js-comment-button').click();
+
+ deferred.resolve(note);
+ const $noteEl = $notesContainer.find(`#note_${note.id}`);
+ $noteEl.find('.js-note-edit').click();
+ $noteEl.find('textarea.js-note-text').html(updatedComment);
+ $noteEl.find('.js-comment-save-button').click();
+
+ const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`).find('.js-task-list-container');
+ expect($updatedNoteEl.find('.note-text').text().trim()).toEqual('');
+ });
+ });
+
describe('getFormData', () => {
let $form;
let sampleComment;
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 13827a26571..2c34402576b 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -51,7 +51,6 @@ if (process.env.BABEL_ENV === 'coverage') {
'./environments/environments_bundle.js',
'./filtered_search/filtered_search_bundle.js',
'./graphs/graphs_bundle.js',
- './issuable/issuable_bundle.js',
'./issuable/time_tracking/time_tracking_bundle.js',
'./main.js',
'./merge_conflicts/merge_conflicts_bundle.js',
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 050170a54e9..540245fe71e 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -22,7 +22,7 @@ describe('Commit component', () => {
shortSha: 'b7836edd',
title: 'Commit message',
author: {
- avatar_url: 'https://gitlab.com/uploads/user/avatar/300478/avatar.png',
+ avatar_url: 'https://gitlab.com/uploads/system/user/avatar/300478/avatar.png',
web_url: 'https://gitlab.com/jschatz1',
path: '/jschatz1',
username: 'jschatz1',
@@ -45,7 +45,7 @@ describe('Commit component', () => {
shortSha: 'b7836edd',
title: 'Commit message',
author: {
- avatar_url: 'https://gitlab.com/uploads/user/avatar/300478/avatar.png',
+ avatar_url: 'https://gitlab.com/uploads/system/user/avatar/300478/avatar.png',
web_url: 'https://gitlab.com/jschatz1',
path: '/jschatz1',
username: 'jschatz1',